2024 Public Training Schedule
November 18 – 21, 2024 – Agile Analysis and Design Patterns
Half-Day Sessions
December 9 – 12, 2024 – Agile Analysis and Design Patterns
Half-Day Sessions
(c) 2024 To Be Agile
Another aspect of using unit tests as specifications is to clearly show what’s important in each test. We do this primarily by naming things well and calling out generalizations and key concepts in the names of the symbols that we use.
Every test has a name but you never call it, the system calls it but that doesn’t mean the name of the test is meaningless. I see beginners sometimes name their tests test1, test2, etc. and I think this is a missed opportunity. The name of the test should express what it is that we’re exercising, the purpose of the test, in plain English, and it doesn’t matter to me how verbose I make the name because I only ever have to type it in once.
However, I hate redundancy, acronyms, and jargon in names so I prefer to omit the “test” prefix that is typically used in test methods of unit testing frameworks. I can clearly see that it is a test method because it’s associated with a test class.
Showing what’s important in the test is about making the test scenario as clear as possible. Typically, a test scenario has three components that we’re all familiar with: the setup, the trigger, and the verify.
Setup is all about changing the system from its initial state into a state that’s ready to trigger the behavior that we want to test. Every unit test runs in isolation and so every test will require its own setup before it can be executed.
The second phase of a test scenario is the trigger. The trigger is the single event or behavior that our test is verifying. We invoke that behavior at this point. This is the thing that we’re testing and so we want to say what were testing here as clearly as possible.
The final phase of our test scenario is called verify and it’s all about checking the behavior that we triggered created the correct results and did not create any unwanted side effects. Verification is typically done by comparing the state of the system against what is expected.
I find that no matter how complex my tests are when I break them out into these three phases, they become much clearer and easier to understand.
I also use constants in my tests, which gave me the opportunity to get rid of magic numbers in my code and in my tests, which make my code in my tests more readable. When I see the number 21 in code I have no idea what it means but if I have a variable called MinimumAge that holds the number 21 then I have a better idea of what it represents.
I’ve always been a big proponent of using good names in my code but before I started doing test-first development I found that it was sometimes difficult to express how I wanted my code to be used in the symbols I used to define my code. When I do test-first development I don’t have that problem because I can name the symbols in my test in ways that express how I want my test to use my code and this helps clarify a great deal so I think of it as a valuable form of documentation for my code.
Note: This blog post is based on one of the “Seven Strategies…” sections in my book, Beyond Legacy Code: Nine Practices to Extend the Life (and Value) of Your Software.
Previous Post: « Use Helper Methods
Next Post: Test Behaviors, Not Implementations »