Do Not Mix Test Code With Production Code
A few months back I reviewed an application. It was sad to see in how little time you could create a mess that is not only hard to work with, but that can only be changed with a lot of effort. In this post we focus on one tiny little detail that can help you as a warning sign early on: having test code directly in the production code.
Why is it bad?
It looks so innocent and is hard to spot, but putting test code in your production projects comes with a long list of downsides. Here are the top 5 reasons I think that is bad:
- You need to reference all the testing libraries (the unit testing framework, the test runner, the assertion library, and more) that add to the size of the application without adding value.
- The test classes are required to be public and with that get mixed with the production classes.
- Builder classes for the tests look like classes to use for production, what results in strange behaviours and hard to track bugs.
- You miss tests because they are not in the test project.
- It is harder to find the right place for new tests when they are scattered throughout the application.
What should happen instead?
I understand that it helps to keep your test code and your production code close when you try things out. I do that myself often, but I do it the other way around in the test project.
I start with the test class and put the code I want to be my future production code below. That way the newest test I write is at the end of the test class next to the “production“ class. This minimises the need to scroll and I can iterate fast. When I have it in a working state, I can extract the “production” class in its own file and move that file to its location in the production project. The refactoring tool in my IDE fixes the imports in the test class for me and we end up with everything in its right place.
Doing it this way speeds up development, but it does not mess up your production code. All you need to do is to tolerate for a few hours that you have 2 classes in the same file.
How to fix the tests in the production code?
I like to get an overview on all the involved tests in the production project to see how bad the problem is. For that we can search for references to the unit test framework. The list we get helps us to figure out where we want to move the tests.
If you have different test projects for unit and integration tests, it can get a bit tricky. If the developer followed naming conventions that make clear what needs to go where, you can filter more detailed and move the test classes accordingly. But expect that the location of the test code was not the only thing that got skipped. In that case move all tests to the unit test project and then in a second round move the integration tests to their correct integration test project.
If we know the general direction in which we want to move the test classes, we can go to the next phase and start the moving. I like to follow this process, but you are free to change it as it helps you.
- Build the project and run all tests to see that everything works. Write down the number of tests.
- Remove all references to test-only packages in the production project. That should produce a lot of errors in all the test classes.
- Go to the test project and create the folder structure you want.
- Move the test classes to the matching folders in the test project.
- Add missing references to the production code projects in the test projects.
- Rebuild everything to check that the references are correct.
- Run all unit tests to check that all still works. Check if the number of tests is the same as it was at the beginning.
Following this process is not complicated, but the more test classes you need to move, the more work it will be. As always, the sooner you notice the problem, the quicker it is to fix.
Conclusion
What starts with the idea of moving faster quickly starts to slow down development. If you have tests scattered throughout the whole application, you need to remember that there are tests in the production code projects and that you need to run them too if you want to make sure your safety net still works. Too often that will be forgotten and then your tests will only catch problems on the CI server – should that look for tests everywhere and not just in the test projects.
The sooner you separate the test code from the production code the better it is. This is a canary in a coal mine and if you ignore this warning, you quickly end up with much worse problems. Then when developers not even can follow the simple rule of putting test code into test projects, you do not want to find out what else they could ignore.