As you might have realized by now if you follow this little blog (and you should), I’m a huge fan of automated tests. If you’re not, I assume it’s because you either don’t find them useful, or you find them hard to add.
Tests are useful because code breaks. My code, his code, everyone’s code breaks eventually (and that includes yours too). What you aim achieve with automated tests is to break your code before you deploy it. It’s much easier to think of a fix when you don’t have people running around with their hair on fire because the site is down. Also, it doesn’t do you any good as a programmer to write code that breaks as soon as they leave your door.
Now that we decided tests are useful, let’s see what we can to make them easier to add.
Write testable code
You should see testing as an integral part of development. In the same way a programming language and/or framework impose some patterns in your code, in the same way the decision that you’re adding tests should change your code. I’m here to tell you there is a way to make your life a lot easier. There are a lot of design patterns out there. Together we are going to riff through a few that should make testing your code a piece of cake.
If your code is easy to test, you will test it more. With this you can ensure a more resilient project in the harsh production environment.
You have more details here. It sounds a bit fancy at first. It’s not. The idea is this: Whenever a function/class depends on another module, they should get that module as a parameter and not instantiate it themselves. Combine that with automatic dependency injection and you’re in business. Mocking and stubbing will never be an issue again. You can completely isolate modules and mock out their dependencies. From then on the sky’s the limit on what you can test.
State is evil when testing
This is not so much a design pattern, as it is advice. Nothing will make your head hurt more than handling global state in tests. Then why don’t you save yourself the trouble and keep the state to a minimum. As a rule of thumb, all functions should be stateless. If called with the same params, then they should always return the same result.
If you remove state from modules, the problem becomes how do you deal with modules that inherently should have a state, things like db connections and network connection. My approach on this is to isolate the state in a shared model. Then pass that model around anywhere it is needed.
Be in the right mindset
Accept the fact that you’re going to have to test whatever you are working on now. Always keep that in mind when writing the code. Thinking How in the world will I test this, is a good way to spot anti patterns and code smells early on.
You have to think about module dependencies and if you should another. Then where should the logic and state live. Every now and then, you get to a point where you need to do some hackish workaround because the testing framework just hates you. That is OK, it’s just like every other workaround you had to do to get past a framework limitations.
These may not all be design patterns. However, I do feel that the advice above covers the gist of what you need to do so that automated tests don’t become a burden. See you next time with some advice on how to write better tests for your code.