So here we are, there are now so many layers of tests that can be performed on a unit of code that even developers asking questions on stack overflow can’t keep them in line, is it a unit, iteration, functional, black box, white box or what ever test, and I think it has began to harm what we are trying to do. Lets face it there are actually only two types of tests:
1. There are the tests that are written by a developer during development that lead to the creation of production code, what I am going to call for this argument “developer tests” because they are written to aid development
2. and there are tests that may be written by a developer, but it can equally be some other third party, that test production code but do not lead to the creation of production code, and for this argument I will call it “functional tests”, because we are testing already written functionality.
And that’s it, all the various layers of testing can be slotted into one of these two categories.
So why is this important?
It goes back to the whole test first pattern of working and a stage that gets forgotten in a lot of the process and because its forgotten its not practiced enough and our code is suffering because of it.
What am I talking about? Refactoring
Remember the test first work pattern
Write a failing test “RED”
Make the test pass “Green”
Then REFACTOR the code!
When you make a change to the internals of your code, how many tests break?
Do you spend more time “fixing” your tests then writing production code?
I think we have all been there at some point, and felt that the tests just knew too much about how the code worked, they had become tightly coupled, did it feel wrong?
When Dan North introduced “Behaviour Driven Development” before all the frameworks were invented to “support” it he talked about how it was a way of helping people discover what tests to write. Even highly skilled developers have trouble getting started when doing test first development, the give the first test a silly name “monkey” until we discover what its doing, just to make headway is not unfamiliar.
I think we have with the need to mock or stub stuff pasted ourselves into a corner which makes it hard to start, Lets talk about behaviour, what is it. how about if we change the question,
What happens if there for the given input there is no output result, or the output result is a fail. if we are working on some update or create feature.
Remember we are working at the boundary here, either at the controller or handler of an API or MVC website application, the execute method of a program etc. What is our code going to do?
Well its either going to return false, nothing an error result or throw and exception.
there is our first test 🙂 Working on the negative case first can help, in getting started, even if we change our minds later and decide that our negative result should do something else, we have a starting point, we now can write a test that checks for our desired result and in the sprit of doing the simplest thing, I tend to plump for returning false, unless we already have a pattern in the application for negative cases.
so at this point we have a test and a handler that has a single method that returns false. Notice no mocks, no tiers in application no service calls. This is one of the most difficult things for developers to do, but if we start doing the simplest thing that can work, we will get working code yes it will all start off in the controller and that’s where the important step of refactoring comes in.
When we have the code working in the controller we can refactor out into services and repositories or what ever pattern the code needs, I would even continue to call these in the test, create real things not mocks right up to the far boundary of the application be that an API client call or the database or file store. Why? I want the tests to run fast, and crossing process boundary’s is the slow bit of any application, so if I can’t get something “in memory” then that’s the point to mock, but the code I am refactoring is going to be fast, it all started in the handler remember.
At this yes I know people will say but that’s not a unit test, but I am not writing a unit test, its a developer test, and its testing a single unit of work isn’t it?
The code all started from a single method call, we refactored it into services to make the code readable, maintainable and hopefully reusable, but it originated in one method, its a single unit of work, I am mocking the boundary to keep the test quick. Remember the tests were green before we started refactoring so the code worked, as we never refractor in red.
we can change the internals of our application without changing any tests the tests are working at the boundary testing behaviour and have no interest in how something works, only in the behaviour it exhibits.
We will actually have fewer tests, and greater freedom because we can now refactor in two directions either pushing code down the application, or conversely bringing code up the application stack or we can refactor across as we do now creating private methods within the application.
Build out the behaviour at the boundary, refactor the code when you have enough code to make it worth while, think clean code, is the method (usually the handler) doing too much? more than one thing? then its time to refactor and push the functionality down your application stack.
It takes practice, and yes your developer tests will cover calls across multiple classes, but remember it is a single unit of work, the code originally started within the one method its doing a single thing, it maybe complex, but it is still a single thing, methods should only ever do a single thing.
Another side effect of working in this way is the only way to create private methods and other tiers of the application is to refactor to them, they are extracted from the boundary code, which also solves the question often asked, how do I test private methods? Answer: you don’t! they are tested by being called by the method from which they were extracted.