Not being able to unit test an essential scenario is one of the reasons developers stop unit testing their code. In fact most of the time when a team decides not to use TDD (Test Driven Development) is because it seems as if they won’t be able to test the production code using unit tests.
Most of the time this is just wrong!
Of course some scenarios are more difficult to test but in most of them it’s not impossible, just a matter of technique and tools.
Unit tests are small, atomic pieces of code that test a single functional unit. The scenario might be difficult to unit test because you’re trying to test too many things at once or because you’re testing to wrong thing.
Not every part of the functionality should be tested – When developing a client component that uses webservices to communicate with a server, there is no reason to test that WCF works. Instead test that your code calls the webservice and act according to the returned response (two separate tests - please).
Try answering three questions in order to decide if the you’re testing the right functionality:
If the answer of the three questions above took more then one (short) sentence – you might be trying to test too much. In that case - break the test to several smaller tests and try again.
In the end you may find some outer dependency that hinder your ability to test your scenario in order to unit test that code you’ll need to isolate your code from that external dependency.
I’m not a big fan of design for testability, I am a huge fan of good design. Some practices of good design also create a more testable code (e.g. code to interface not implementation) and should be used to make the overall development process easier.
I found that the easiest way to design testable code is to write the tests first – TDD style.
Use dependency injection if it make sense in your production code and follow the principles of good design and your code will be more testable but don’t overdo it – design your code for maintainability not for testability.
Requirements changed or you’re writing unit tests for code that was already written and suddenly you discover that some external dependency (e.g. Database, 3rd party tool etc.) prevent you from writing your test.
No problem - because your code is in fact your code you can change it to make it more testable.
Refactor a class constructor to receive additional parameters or add an internal property to replace the external dependency with a dummy class.
Isolation frameworks were created so you won’t have to write additional code (or change your existing code too much). They create fake objects and set their behavior and some can even set behavior on existing objects (and even these “evil” static methods).
In a nutshell fake objects help you isolate your code from external dependencies without the need to write a bunch of dummy classes.
There comes a time when it’s no possible to unit test a scenario but before you give up there is one other alternative - Integration tests.
Instead of testing a specific isolated part of your system – why not test system wide functionality?
You’ll probably need and might need setup (and teardown) but you’ll end up with an automated test just as well.
In the vast world of software development there is still code which you could not unit test but I hope that I managed to convince you that most of your code (if not all of it) is testable, sometime writing unit tests is another problem that you need to solve as part of your work.
In future posts I hope to cover each of solution above and how they can be used when writing unit tests.
Labels: Mock objects, Reflection, Unit tests