It is interesting to see what is called a “unit test” depending on who you talk to. Tests have had their definitions muddled quite a bit. I think this is partly due to the fact that tests are difficult or seem to be extra work that is unnecessary. Writing a test that is easier to write, and calling it a unit test, can be a way to say “There, I have unit tests now,” instead of doing the complete work of isolating your code and writing a true unit test.
So, what is a unit test? To me a unit test is a method that tests an individual class or function and no more. It is also a test that runs quickly for frequent validation. What benefit does this give you? It gives you the ability to focus on what you want this class or function to do. If you find yourself having difficulty isolating the class, or having to do a lot of setup to test a class, then you need to look at re-factoring your code. Tests are good at showing you ugly code, and showing you how you can clean it up. Having a test that runs quickly allows you to know immediately if you have broken other scenarios after making changes to your code.
By cleaning up your code I mean writing smaller methods, smaller classes, and having fewer side effects in your code. Most programmers don’t think this way when writing code initially, including me. I could write a lot about this subject, but I think I’ll wait until another post. Suffice it to say unit tests are good at forcing you to write clean readable code. This code would be a pleasure for another programmer coming behind you to work on.
If you are thinking this is hard, you’re right. But the long term benefits far outweigh any discomfort now. Those benefits include:
- Readability of your code for anyone coming after you.
- Smaller classes and functions that do what they say they should do without a lot of side effects.
- Code that you have confidence in because you know your unit tests pass.
- The freedom to refactor your code and know quickly whether it still works because of your confidence in your tests.
So that is the best case scenario. What I’ve seen a lot of, is tests that are called unit tests, but are actually component or integration tests. These tests allow the class to call a database without mocking out those calls. They perhaps call a web service layer instead of the class on the other side of the web service. Don’t get me wrong, these are good tests, they are just not unit tests. I think a lot of development teams call these unit tests because it is hard to isolate a class or refactor it.
Refactoring your code is really the best option here. Jumping to isolate your code can hide problems in your code and make your tests more complex. However, in certain cases like database calls or web service calls, it makes sense to mock calls coming from the class you are testing. There are plenty of tools out there to help you isolate your code like moq or TypeMock.
To boil it down, unit tests are the bottom layer of tests written by programmers and for programmers. They are not meant to test an entire business case. They are meant to make sure that, at the lowest level, your code does what you expect it to do. The other types of tests should rest on this base, and test the things that unit tests cannot. I would categorize these additional tests as component, integration, acceptance, and manual tests. I hope to go more in depth on these types of tests in a later post.