Last time I wrote about how we are increasing the numbers of tests and how they are damaging our code. Another problem we have that is stopping us getting the full benefits of refactoring code is the proliferation of mocks within our tests, this almost forces the code to mimic the production code epically when using some of the modern behaviour test frameworks that helpfully mock all the dependencies of a class.
Why is this bad? before answering that lets first think about why we mock, in fact to forestall any arguments lets call this mocking or stubbing out of things fakes.
Why do we fake things? When test driven development was introduced by Kent Beck some 16 odd years ago, computers were a very different think, I was writing code in visual basic 6, on Windows Server NT, for the dot net world testing started with the release of NUnit for the dot net framework, this was around 2002, computers were a lot slower, so in order to keep tests fast we faked the slow bits.
One of the core tenants of test driven development was and is the tests must be quick, we are running all of them all the time, so they have to be very quick, this was the primary reason to fake some components. Move on to today and we are faking more and more of the systems, not just the boundaries but between layers and I think it is this that has made our tests fragile and reduced the chances of refactoring.
I think in order to progress and improve the development experience and gain the benefit of refactoring we need to go back to the origins of the practice, back to Kent Becks original idea.
1. Write the failing test, along with enough production code to make the test fail, what do I mean, we want to write the test so it compiles, runs and fails, this will involve writing some production code, creating a new method etc, the test will be red and it will be obvious why the test will not pass.
2. Now we need to get to green as quickly as possible, this is where we cheat, lie steal and plagiarise code, copy pasting where necessary, my policy is we should stick it all in the one place the method we are testing, don’t worry about design or style get it working make the test pass, get to green as quickly as possible.
3. Once the test is green, we hit the stop button and review the mess we have just made, what is wrong, how many things is our method doing, can we see any duplication, and we then refractor. Notice we do not create code at this point, we are extracting classes methods etc from the code we have written, the test will fail if we break something and even if you add three levels of dependencies during the refactoring pushing concepts down the stack, it all started in that one place, so by definition it must be tested! Also notice I have said nothing about faking anything, I would write to go right through the stack at this point calling the database or other services as required, only when I have finished refactoring would I stop again and run my tests to check for speed and then and only then would I introduce a fake object right out on the boundary where the test slows down.
Doing this means that the next test I write will go though the same process, and because we are only mocking where we actually have to, in order to keep the tests fast we can refractor the hell out of the internals of our application all the time knowing because we will have 100% code coverage that we have not broken anything.
If we build the whole application like this our tests will look very different to the implementation, in fact we may have fewer tests than using the modern practice and because we are only using fakes where necessary to improve the speed of the tests the internals of our application are open to complete refactoring without danger of breaking tests. In fact the tests are now an asset changes can be introduced using the same technique and because each test is covering a specific behaviour the number of tests failing because of change, not because of broken code should be greatly reduced