In a perfect world, specifications are clear and concise. Developers take those clear and concise specifications to write unit tests before writing the code that does the work. Then it’s red -> green -> refactor -> Bob’s your uncle you have good code!
In the real world, you could end up writing tests after the code has been written for whatever reason. After doing this poorly recently, I’ve come up with some of my own guidelines for unit testing:
- If you’re doing too much setup, you’re testing the wrong thing: The first sign of too much setup is that you’re hooking your test code up to outside services. Do not test outside services in unit tests. Someone else already unit tested that service. You don’t have to do it.
- Find the code that does the work, put it in its own method if needed and test that method: You’re testing code that likely was not written with testing in mind. Code that uses another library or service may be intertwined in the logic of the code you’re writing. Separate the unique logic that you (or the original developer) wrote into its own method then you only have to worry about testing that method.
- If the method you’re testing must use an outside resource, abstract the outside resource into an interface: Let’s say the code your testing reads from a file or connects to an API or listens for an event. It’s impossible to test this code without that outside resource, but it’s also unwise to assume that the outside resource will always be available when the test is run. Abstract that outside resource into an interface. Then your implementation of that interface for your tests can point to a local resource that will always be there! Now your tests can run anywhere and your working code can still do its thing!
It all boils down to 2 main points. Make sure you are only testing your unique code and make sure the resources you need for testing will be always available no matter where your tests are running.
What are your tips for writing (or refactoring) code to make it easy and consistent to test?