I find it surprisingly hard to get new developers to really engage with unit testing but then when I think back to when I had just graduated and what I thought about unit testing (I'm here to write code, not to test!) I shouldn't be surprised.
Sadly unit testing is one of those counter intuitive processes that actually speed up the development once you get it up and running but if you don't believe me (shame on you!) let's go through the benefits.
- Discover bugs quickly
- Easier refactoring
- Better integration
- Living documentation
- Better design
- Save time
- Good feedback
- Prevent feature creep
- Avoid embarrassment
Discover bugs quickly
A unit test will often be created around the time that the method under test is being created (either before or after). By being disciplined and ensuring that all our tests for that method all pass before we move onto another method we can greatly reduce the chance of introducing a bug into our code that will later prove to be costly and time consuming to locate and fix. When we write unit tests around the time that we write the method we double check that we are satisfying all the requirements in that specification for that method, which while not a coding bug as such is still a defect in our application if we ship it stating that it does something that it turns out it can’t.
Often the main argument for introducing unit testing is to help find bugs and while that is a very important part, the main strength of unit testing is the confidence it gives to alter existing classes and method without introducing a regression. Without unit testing refactoring an existing class or method can be a very time consuming and difficult process. Without unit tests after refactoring a method you would then need to manually check that the change did not resulted in a bug being introduced into another part of your application that was previously working usually through exercising the UI which is more time consuming than running a test suite and more prone to error or misunderstanding the test steps. By using unit tests we can ensure that the class or method that we changed still works from the outside (black-box) the way that it did before we refactored. This is especially useful when having to maintain an application that you did not create.
By unit testing our application we can have confidence that the individual classes and methods in our application work in isolation, when we then combine these individual parts together we should have fewer issues than without unit tests and be able to more quickly identify these issues
A suite of unit tests allow for a living document to be created that helps new developers to the project gain an insight into how the application behaviors and shows not only the “happy paths” through the application but also the “unhappy paths”. As the unit is tightly coupled to the application, it is more likely to be maintained and more accurate that the official documentation which (sadly) is often a few versions behind the current state of the application.
In order to get the most out of unit testing you need to design your application to accommodate unit testing (while you can retrofit unit tests into an existing application it is often better have unit test in mind when you create your application), by doing so you can often eliminate certain bad smells from your application as a bonus e.g. if we have a method that is dependent on a value stored in session object then rather than have that method access the value directly from the session we would design our code so that that value was passed to the method as a parameter. This would result in producing an application that is more decoupled. Unit test help with this decoupling as in order to unit test that method we would need to mock the call to the session object to isolate and control its response however by removing the call and passing the value as a parameter we can write quicker unit tests and more decoupled code.
Which one would you say is easier to code and write a unit test for? You will often find that if a method is difficult to write a unit test for it means that its not designed properly. A key consideration when writing software for any platform is: can I abstract this method/class out and reuse it another settings? If you can’t it may be worthwhile to spend the time removing whatever is preventing so that you produce code that is loosely coupled and highly cohesive. However you always need to avoid the temptation to begin writing a framework that you don’t need.
Unit tests give us a suite of regression tests that we can run whenever we make any changes to the application. This is especially useful if the original developers of the application are no longer involved. If we find that a bug got out into the “wild” then all we did to do is create a new failing unit test and alter the method until we that unit test passes. This unit test will then ensure that going that going forward we should never regress and introduce that bug again.
A unit can be ran at the speed of your CPU that operates much more quickly and reliably than a manual tester. Depending on the size of your test suite you should be able to run it multiple times a day giving you near instantaneous feedback as to whether your changes are having an adverse effect on another area of application. Now this is not to say that you don’t need to still have manual testers but unit tests should mean that the quality of application that you give them should be higher than if you didn’t have any unit test. Just think what you could do with the time saved filling out bug reports for minor bugs because you used a “greater than” rather than “greater than or equal to”!
Feedback comes in two forms during writing the unit test and during running the unit test. We have already spoken about the writing feedback in Better design so this section covers the other type of feedback. When a unit test fails you know exactly which one failed, what the systems pre-conditions where before the test was executed, able to recreate the failure by rerunning the unit test and should be able to more easily identify the cause of bug than if you had to rely on a person to discover the bug or read another bug report that only states “Your app crashed!!”.
Prevent feature creep
Depending on what development methodology you are using to create your application you will either have to write your unit tests before or after you write you methods. Unit tests help to prevent you from introducing unnecessary features into your application because every new feature that you add needs to be fully unit tested and that suite of unit tests need to be. All of this testing of unnecessary features takes time and effort.
No one likes to be told that something that they have helped created does not work. By introducing unit tests (provided they are good) you should greatly reduce the chances of this happening and avoid the embarrassment of having to tell your boss why this happened.
But there must be a downside of Unit testing?
We’ve read a lot about the benefit of unit testing however we need to balance the argument (however briefly). The main downside of unit testing is the time involved to show the benefits of unit testing (both to the developers and more importantly project managers), to teach how to write effective unit tests, the actual writing of the unit tests themselves and maintenance of the unit tests.