Thinking about positive and negative execution cases for your system, and then specifying them to an extent that they can be implemented requries quite a lot of cognitive work.
And after you’ve done that, the tedious tasks start.
You have to set up and structure your testing code, implement test cases, debug and update them every time that you make a change to your source code.
That is why you want to be as efficient as possible when it comes to unit testing.
In this article, I show you my top 5 hacks that I use everyday to save as much time as possible.
1. Use the right methods
Most effort in unit testing can be saved by simply doing it right (which most people don’t).
2 methods that will already save 60 % of your time are Equivalence Class Partitioning and Boundary Value Analysis (BVA – You can read more about how you can use them to be more efficient in the following post:
“2 methods that help you save 60 % of your effort in Unit Testing”). In their essence, these techniques help you identify the most efficient test cases, so that you never have to write unnecesary unit test, ever again!
The AAA-pattern is another tool helping you to reduce your effort when it comes to maintaining your test cases, and still understanding them several months after they’ve been created. It essentially allows you to structure every single unit test in the same way, by dividing it into three blocks (one for Arranging the test setup, one for Acting, and one for Assertions). You can read more about the AAA-pattern and particular C# examples for it in this post:
“The AAA Test Pattern explained with C# and NUnit“.
2. Automate as much as possible
When requirements are specified properly (e.g. using EQ-method), unit tests can even be derived automatically (e.g. using BVA).
In a good test automation tool, generated tests should already implement patterns such as AAA or consistent naming of folders/test classes/test methods. With that, you can not only save time when creating unit tests, but this makes maintaining your created test cases very easy on the long run. Best practices used by modern test generation tools are optimized for making it as easy as possible to (i) navigate your test cases, and (ii) understand what they actually do (even if you haven’t been the one that created them in the first place). By ensuring that generated code automatically sticks to best practices, you can automate the tedious work of learning all those best practices and actually adapting them to every single unit test that you write.
3. Use a Test Gate
With a test gate, you can ensure that for every commit, all unit tests must pass before the changed code can be pushed to the central repository. This is already supported by most build tools today.
A test gate not only forces you to always keep your unit tests up to date (without that, your unit tests actually have no value at all). Most importantly, it gives you the security that you did not unintentionally break the whole system with your changes – this is what unit testing should be about – with maximum automation support!
4. Use the full power of your unit tests
Most people think they write unit tests for one reason: finding bugs in their implementation.
Besides that, you can use unit tests as a way to think about and write downn what your tested code unit should actually do and don’t do, before even implementing a single line of code (this is TDD in a nutshell…).
Additionally, unit tests can be used as documentation of your code. By going through your unit tests, it should be cristal clear to someone from outside the project how your implemented system should look like (to achieve that, you have to use the right methods in the first place, as mentioned in Hack #1 above).
5. Evaluate your Test Cases
When looking at the testing folder projects, you often see two extremes.
Most of the time, there are hardly any unit tests in place. But in many cases, there are so many unit tests, that (1) developers have a hard time to still understand what they actually do, let alone thinking about maintenance and updates, and (2) most of them are redundant, outdated, or are created just out of beauty – but will never detect any actual defect.
Finding this sweet spot where minimum effort meets maximum value is one of the big challenges in unit testing.
But having the right data to support your decision on whether to add more test cases definitely helps.
Although whitebox-based evaluation techniques such as code coverage are flawed (see why in this example:
“Limits at which code coverage fails catastrophically”), they give a good hint on how much unit testing should still be done in your project. If you are more serious about predicting the value of your test cases, mutation testing (see article “Define once, reuse always”) should be the right fit for you.
In this post, I showed you 5 particular hacks that you can apply today to do better unit testing.
You can always share your experiences/opinion with me in the comments section below!
Daniel Lehner