Unit testing is (or at least should be) an integral part of every software project.
Why?
- Running your unit tests after changing your code gives you the security to know whether (and how much) you broke some unintended parts of your system.
- On the other side, by making your unit tests pass again after a code change, you can automatically ensure a certain functional quality of your system.
But this only works if (i) you actually have a sufficient amount of unit tests in place, and (ii) they actually represent your up-to-date requirements.
However, consistently writing and maintaining your unit tests is a hard and boring task.
That’s why I will show you 3 strategies that I use to be more efficient and smart when it comes to unit testing in this article.
Think, Specify, Code
But in its essence, (1) Think (2) Specify (3) Code is simply about the following:
- Before writing any line of code, you try to envision how your finished system looks like in as much detail as possible. Think of a user interacting with it: what would he or she expect the system to look/work like? What are cases that should be avoided at any cost?
- You specify your thoughts in a structured way. This can be by writing them down on a sheet of paper, or (more preferably) using a structured syntax and framework that forces you to be as specific as possible. The most appropriate way in my opinion is to create models of your problem. Want to know why? Read the following post for understanding the “power of models” in software engineering.
- In a last step, you simply transform your structured thoughts into executable code. Of course, the more systematic your description in step 2, the easier it is to derive the actual implementation from it.
Generate Test Code
If you’ve specified your thoughts about your code to a sufficient extent, you can even automatically generate the code for your test cases from it. The #1 rule for code generation however is that defining your generation base (i.e. the information that is used to generate your test case code) must always be less effort than writing the generated code yourself. Writing test cases, but with a different syntax, and then generating your NUnit/xUnit/MS-Test syntax from it, actually has no real value at all. Techniques such as equivalence class partitioning and boundary value analysis are a perfect fit for specifying the generation base for your test cases on a level that allows maximum automation and efficiency potential. You can read more about these techniques in the following article:
“2 methods that help you save 60 % of your effort in Unit Testing”
Add a Test Gate
Make sure that you never push code without all unit tests passing. You can ensure that in most build tools using a test gate (you simply configure that all your tests are run after check-in, and that they all pass).
Why? The #1 problem in unit testing is that after some time, you don’t trust your unit tests any more – because you never bothered updating them in the first place (see this article for more details:
“5 Signs that you’re doing Unit Testing wrong (+ actions to change that)”). Of course, you have to put in some effort for keeping your unit tests passing. But the benefit and security of actually knowing the impact of every code change to the rest of the system outweighs these costs by far. This is how you do better unit testing!
What do you think of these strategies?
Do you have additional personal strategies to add?
What are your personal experiences with improving unit testing in your projects?
Let me know in the comments section below!
Daniel Lehner