Maintainable Unit Tests
"as soon as the requirements started changing and I started doing refactorings I found that I spent more time rewriting / fixing unit tests"
So? How is this a problem?
Your requirements changed. That means your design had to change. That means your tests had to change.
"I spent more time rewriting / fixing unit tests than I did writing code, much more time in fact."
That means you're doing it right. The requirements, design and test impact was all in the testing, and your application didn't require much change.
That's the way it's supposed to work.
Go home happy. You've done the job properly.
Practice
It takes a while to learn how to write decent unit tests. A difficult project (more like projects) is nothing strange.
The xUnit Test Patterns book recommended already is good, and I've heard good things about the book you're currently reading.
As for general advice it depends on what was hard about your tests. If they broke often, they may not be unit tests, and more so integration tests. If they were difficult to set up, the SUT (System Under Test) could be showing signs of being too complex and would need furthering modularisation. The list goes on.
Some advice I live by is following the AAA rule.
Arrange, Act and Assert. Each test should follow this formula. This makes the test readable, and easy to maintain if and when they do break.
Design is Still Important
I practice TDD, but before any code is wrote I grab a whiteboard and scribble away. While TDD allows your code to evolve, some up front design is always a benefit. Then you at least have a starting point, from here your code can be driven by the tests you write.
If I'm carrying out a particular difficult task, I make a prototype. Forget TDD, forget best practices, just bash out some code. Obviously this is not production code, but it provides a starting point. From this prototype I then think about the actual system, and what tests I require.
Check out the Google Testing Blog - this was the turning point for myself when starting TDD. Misko's articles (and site - the Guide to Testable code especially) are excellent, and should point you in the right direction.
I’m a huge fan of unit testing but have experienced problems with TDD (or basic unit testing for that matter) on my most recent project. After conducting a post implementation review I found that we (me and the rest of the team) faced two main problems with our implementation/understanding of TDD and unit testing.
The first problem was that we faced was that we didn’t always treat our tests as first class citizens. I know this sounds like we were going against the philosophy of TDD but our problems came after we’d done most of the initial design and were hurried into making on-the-fly changes. Unfortunately due to time constraints the later part of the project became rushed and we fell into the trap of writing our tests after the code had been written. As the pressure mounted working code was checked into source control without checking if the unit tests still passed. Admittedly this problem has nothing to do with TDD or unit testing but was rather the result of tight deadlines, average team communication and poor leadership (I’m going to blame myself here).
When looking a little deeper into the failing unit tests we discovered that we were testing too much, especially considering our time constraints. Instead of using TDD and focusing our testing on code with a high return we were using TDD and writing tests for the entire code base. This made our proportion of unit tests to code much higher than we could maintain. We (eventually) decided to only use TDD and write tests for business functionality that was likely to change. This reduced our need to maintain a large number of tests which for the most part very rarely (or never) changed. Instead our efforts were better focused and made for a more comprehensive suite of tests on the parts of the application we really cared about.
Hopefully can learn from my experiences and continue to develop TDD or at the least still develop unit tests for your code. Personally I found the following links extremely useful in helping me understand concepts such as selective unit testing.
- blog.stevensanderson.com
- blog.tatham.oddie.com.au