Archive for the ‘unit-test’ tag
Writing unit tests can be fun
I recently came across Pavel Brodzinski's blog and while browsing through some of his most recent posts I came across one discussing when unit testing doesn't work.
The majority of what Pavel says I've seen happen before on projects I've worked on but I disagree with his suggestion that writing unit tests is boring:
6. Writing unit tests is boring. That’s not amusing or challenging algorithmic problem. That’s not cool hacking trick which you can show off with in front of your geeky friends. That’s not a new technology which gets a lot of buzz. It’s boring. People don’t like boring things. People tend to skip them.
I think it depends on the way that the unit tests are being written.
When I first started working at ThoughtWorks I used to think that writing tests was boring and that it was much more fun writing production code. A couple of years have gone by since then and I think I actually get more enjoyment out of writing tests these days.
There are some things we've done on teams I've worked on which contribute to my enjoyment when writing unit tests:
Small steps
While working on a little application to parse some log files last week I had to implement an algorithm to find the the closing tag of an xml element in a stream of text.
I had a bit of an idea of how to do that but coming up with little examples to drive out the algorithm helped me a lot as I find it very difficult to keep large problems in my head.
The key with following the small steps approach is to only writing one test at a time as that helps keep you focused on just that one use of this class which I find much easier than considering all the cases at the same time.
The feeling of progress all the time, however small, contributes to my enjoyment of using this approach.
Test first
I think a lot of the enjoyment comes from writing unit tests before writing code, TDD style.
The process of moving up and down the code as we discover different objects that should be created and different places where functionality should be written means that writing our tests/examples first is a much more enjoyable process than writing them afterwards.
The additional enjoyment in this process comes from the fact that we often discover scenarios of code use and problems that we probably wouldn't have come across if we hadn't driven our code that way.
Ping pong pairing
I think this is the most fun variation of pair programming that I've experienced, the basic idea being that one person writes a test, the other writes the code and then the next test before the first person writes the code for that test.
I like it to become a bit of a game whereby when it's your turn to write the code you write just the minimal amount of code possible to make the test pass before driving out a proper implementation with the next test you write.
I think this makes the whole process much more light hearted than it can be otherwise.
In Summary
The underlying premise of what makes writing unit tests pretty much seems to be about driving our code through those unit tests and preferably while working with someone else.
Even if we choose not to unit test because we find it boring we're still going to test the code whether or not we do it in an automated way!
What makes a good unit test?
Following on from my post around the definition of a unit test, a recent discussion on the Test Driven Development mailing list led me to question what my own approach is for writing unit tests.
To self quote from my previous post:
A well written unit test in my book should be simple to understand and run quickly.
Quite simple in theory but as I have learnt (and am still learning) the hard way, much harder to do in practice. Breaking that down further what does it actually mean?
Intention revealing name
There was some discussion a few months ago with regards to whether test names were actual valuable, but as the majority of my work has been in Java or C# I think it is very important.
I favour BDD style test names which describe the behaviour of what we are testing rather than the implementation details. For me naming the tests in this way allows people who look at the test in future to question whether it is a valid test as well as whether it is actually doing what it claims to be doing.
No clutter
If we can keep tests short and to the point they are much easier for the next person to read.
To achieve this we need to ensure that we keep the code in the test method to the minimum, including putting object setup code into another method so that it doesn't clutter the test and only setting the expectations that we care about if we are using a mocking framework.
This is made much easier by the Arrange-Act-Assert approach being followed by mocking frameworks nowadays. I think this approach maps quite nicely to the Given-When-Then BDD syntax as a nice way of defining our tests or examples in BDD land.
Don't remove all duplication
While removing duplication from code is generally a good thing I don't think we should apply the DRY principle too judiciously on test code.
As Phil points out this can make tests very difficult to read and understand. I tend to favour test expressiveness over removing all duplication.
One behaviour per test
I used to try and follow the idea of having only one assertion per test but Sczcepan's idea of testing one behaviour per class is much better.
This is one part of writing tests where we should stick to the Single Responsibility Principle in as far as not overloading the test with assertions which then make it more difficult to work out where the code failed if a test fails.
Expressive failure messages
When using JUnit or NUnit for assertions in the IDE the assertion failure messages don't really make much difference because we have the code fresh in our mind and it's only one click to get to the failure.
If an assertion with either of these frameworks fails on the build on the other hand it's much harder at a glance to tell exactly why it failed. This is why I favour Hamcrest which tells you precisely why your test failed.
In Summary
For me the key with unit tests is to make sure that other people in the team can read and understand them easily.
No doubt there are other ways of ensuring our unit tests are well written but these are the ways that I consider the most important at the moment.
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.