If done correctly, unit tests can significantly increase your confidence and code quality as you develop software. The art of writing effective and comprehensive unit tests is very important when creating quality software. It is a skill that takes lots of practice and discipline. When broken down, there are three main attributes that make a good unit test suite.
The first attribute is learning to cover every case for your test suite and ensure each case has a separate test. Secondly is not only to test the happy path cases but also to test every place an error can occur. The final attribute is to isolate the tests, mock out all external services, and return the response is required for each specific test.
Cover Every Case
A big mistake I see when people are building out unit tests is that they don’t test every case. They think that as long as every line of code is touched and the test coverage is high, the tests are doing their jobs! This is not the case; you should have multiple tests, each with a single set of assertions in mind.
Each area of the code you’re writing tests for should have a few happy path assertions and a few tests that deal with exceptions. This will vary on your code’s complexity and abstraction but is a good general rule of thumb.
Along with testing every case, it is truly essential also to test all the possible areas of failure in your code! If you have a SQL query, what happens if the query returns no results? If you have an API request, what happens if the request times out? Testing failures are chronically overlooked when writing comprehensive tests.
These sorts of tests will often lead to discussions on how you should react to failures instead of letting exceptions fly, or worse, catching them and doing nothing. It’s in our nature to think about the happy path when it comes to software, but it is vital to make sure that the failures are handled as gracefully as possible.
Mock External Services
External services are a large part of most software projects. That means it’s essential to know how to handle them when writing tests. 99% of the time, you don’t want to be hitting these services when you’re writing and running tests, and that 1% of the time you do, I’m sure the external service won’t be happy about it.
Being able to mock out external services effectively is a beneficial skill to know and will expand the areas of your codebase that are available for testing. It’s impossible to say precisely how you should mock out these services because it depends on each specific project and language. Still, there are standard practices in every programming language to do this. Two common approaches used in quite a few languages are dependency inversion and mocking objects or functions.
Writing effective unit tests isn’t easy; it requires practice and discipline, but if you follow the steps above, you will be well on your way to creating much more valuable tests and be able to write software with more confidence.