What is a unit test?
One of the questions which came up during the Sydney Alt.NET User Group meeting at the start of October was around what a unit test actually is.
I suppose the somewhat naive or simplistic definition is that it is just any test written using an xUnit framework such as NUnit or JUnit. However, integration or acceptance tests are often written using these frameworks so this definition doesn’t hold.
While discussing this last week a colleague came up with what I considered to be a very clear yet precise definition. To paraphrase: ‘A unit test has no dependencies’.
This means that if the class that we are testing does have dependencies then we need we need to remove these from our test either by using a mocking framework or by stubbing them out.
Dependencies might include calls to a database, web services, 3rd party APIs - we don’t want our unit tests to rely on these being available in order to execute our test.
Why should I care?
If we depend on things outside of our control then we are making our tests fragile and unrepeatable - if a test fails because a dependency outside our control is unreliable we cannot fix it easily.
There is definitely room for integration tests in a system but we can gain much more benefit from them when this integration is not mixed in with testing the functionality of a single unit.
The goal with a unit test is that we should be able to start up our IDE and run the test - there should be nothing else to setup to make this happen.
The grey area
The grey area that I have noticed is around file system interactions in unit tests.
I wrote previously about a way that I have seen using for testing file system operations but I have often written tests which load test data from an XML file before loading it into the test.
Doing that creates a dependency on the file system although it makes the test a lot cleaner than having a huge string containing all the data. If the file is included as part of the project then I think it doesn’t necessarily have to be a problem.
What makes a good unit test?A well written unit test in my book should be simple to understand and run quickly. This is especially helpful when we are practicing TDD as it allows us to keep the cycles between writing code and tests very small.
My colleague Phillip Calcado has a post about an approach to make the former happen but the final word goes to Uncle Bob who suggests the F.I.R.S.T acronym in Clean Code to describe what well written (clean) unit tests should look like:
- Fast - they should ruin quickly. This encourages you to run them often.
- Independent - they should not depend on each other. It should be possible to run the tests in any order.
- Repeatable - it should be possible to run them in any environment. They should be environment independent.
- Self-Validating - they should either pass or fail.
- Timely - they should be written in a timely manner i.e. just before the production code is written.