My 2 cents about Tests
If you are, or have being, in any of the following situations, this post is trying to talk to you:
- Doesn't like creating tests.
- Wants to convince someone to create tests.
- Wants to create a testing culture in your environment.
I will try to understand things that a team needs in order to have a tested project.
In my career, a tested codebase was always a desired state. I knew that it was a good thing, but had never actually seen a well tested project in production. Not that the projects were bad though, I’m fortunate to have worked in excellent projects with amazing people, but the feeling that we could do better with tests was always with me. Until the end of 2019, when I got the chance to work in a place where I could see a tested codebase, and where tests are as important (or even more) as the code itself and I got some lessons from that. I will point 3 main reasons that in my opinion made the difference in order to have a tested project:
- Tooling and Pipeline
It is impossible to have a tested project if it was not designed for that, and if a project is hard to test it probably could have a better architecture. I have worked in projects that it was necessary a lot of juggling in order to be able to test the code, mock huge states, test lots of different possible paths, among other things. One thing leads to another, if you're finding it too difficult to test the code, that's definitely a code smell. Also there is a clear relationship between code readability and testability. If it is hard to test a piece of code, because it has multiple possible paths or because it can deal with multiple different states, it probably won’t be easy to read as well (bugs alert).
Things like, well defined interfaces, pure functions and dependency injection, started making sense to me once I understood the benefit I could take from them when creating tests. One of the most beautiful things in programming is when you can isolate a pure function and can create tests to cover all possible inputs. Or how to mock stuffs if it doesn't have well defined interfaces or doesn't have the dependencies injected? There are lot of different patterns and designs that can be used in order to improve a project testability, so I believe that there is no formula or receipt to be followed, it is something hard and that requires practice and seniority.
And what about projects that are in productions but are not ready to be tested? I really don't know. This is an extremely challenging task that requires hard work and patience. With a small research on google, I could find some discussions about that, that related to some books that covers this topic, mainly the one called Working Effectively with Legacy Code. This discussion has some interesting insights about it https://stackoverflow.com/questions/1541568/adding-unit-tests-to-legacy-code.
Pull Request + Code Review
Just creating tests is no guarantee that the project will be tested. It is necessary to have a culture of creating tests, this need to be in the core of the project and a requirement to its contributors. A situation that I have seen several times is doing a task-force to create tests to cover as much as possible and get to that desired test state of a beautiful world with no bugs and peace of mind. And it usually last until the next commit, and after a while there are a lot of untested code again. This happens as not everyone is onboard with the testing culture.
For me, it was really important to use Pull Requests (PR) and Code Reviews as a way to ensure that the code that will go to production is efficiently tested. Currently, it is unimaginable to create a PR with no tests, it makes no sense since a peer will have to approve it, and test is usually the first thing to look in a review. So, in a small amount of time, I was infected with the culture of creating tests, it was natural and nobody had to teach me. Also reviewing pull requests taught me a lot about how to create one. I learned how to read the code as a story, starting from the integration tests showing an overview about what is being done, to the code it self checking if it makes sense and if it has good namings, and ending at the unit tests to check that it covered all the logics.
Again, what about teams that do not work with pull requests? There are tons of different structures, so it depends a lot, but I believe that the key here is to trust on the development team. To review a code is an extremely important task, that requires attention, technical knowledge but it mainly depends on context. Context to understand what is being built. We say that the reviewer is as committed to that code as the one that made the PR.
Tooling and Pipeline
There are a lot of needs that a programmer have in order to be able to create tests, some examples are: Mocking and asserting side effects; Mocking boundaries; Easily assert expected results; and Having fast and readable feedback. And there are several frameworks to help with those needs, JUnit and Jest are good examples of well stablished tools that makes testing much easier. Understanding how to use testing tools is essential.
Trainings and tutorials are also fundamental in order to guarantee that programmers know how to use the tools, and have a pattern and a structure to follow. This should lead to a codebase full of good tests. However, if the developer still struggle to create a test, there should be a channel to discuss about it, share best practices and find good solutions.
Finally, and really important. Automatic tests have to be in the continuous integration (CI) pipeline. A pull request has to break if any test fails. That's the guarantee that your system is protected, and that the code that goes to production is working. I confess that I have zero experience in creating pipelines, I have never had to create one, but I know that there are a lot of well stablished tools like Jenkins, Tekton and Github actions.
I do believe that it is really hard to have a well tested project if it lacks in any of the covered topics, it might even go well for a while but it won't scale. If I could talk to myself from the past, that guy that wanted to do more about a project testability but with no idea where to start, I would say:
Only commit code that is tested, do your best to have as much coverage as you can. And if it is being too hard to create this tests, it might be an architecture issue or lack of tooling. Also, always ask for a review (not necessarily in a PR, you can just ask your buddy to take a look) before putting the code into production. Be evangelist about testing, spread the word.