If you are new to writing unit tests, here is a list of common beginner’s mistakes. As Warren Buffet famously said “It’s good to learn from your mistakes. It’s better to learn from other people’s mistakes”.
Methods should tackle one behaviour wherever possible
New developers often write bloody great functions that are hundreds of lines long. These functions are usually very hard to test as they do too many things. As a result, it’s hard to test a single behavior.
The right way forward is to (refactor and) make sure each method does a small unit (pun intended) of work.
Uncle Bob Martin, the man behind Clean Architecture, says that “functions should be as small as possible but less than 20 lines of code”. I would say this doesn’t have to be taken seriously, but it is an important guiding point.
Split out external services
Make sure that external services such as API and database calls are written in their own, separate architectural layers. A method dealing with business logic should not, itself, be making database or API calls. There should be another function for this. For example, all database related function should be in a repository or database layer.
The benefit of this approach, other than making your code testable, is that those external services can be mocked.
Don’t be obsessed with code coverage
Coverage is just one metric for your tests and this cannot be your sole aim. 100% code coverage actually says very little about your code quality. You should use this metric not to measure what has been covered but to understand what has been left out, and if it needs to be tested as well.
You might want to read Obessed with code coverage?
Name your tests well
If the test name is bad, confusing or even outright illogical, then readers will need to read the actual test code implementation t understand its purpose. Having understandable test names can save a ton of time.
Forgetting the essence of readability
The code we write is more often read than written. That’s why it’s essential to produce code which readers, usually not the author and often many years later, can understand.
This is true of all code. In the case of tests, we should separate the data creation, actions and assertions in the test. There is a pattern for this known as AAA - Arrange, Act and Assert.
The Arrange section is where you set up an object which needs to be tested, the Act section is where you execute the test, and the Assert section allows you to make claims about the outcome.
Learn the SOLID principles and Clean Architecture
SOLID and Clean Architecture are not for novice coders. However, when you have progressed beyond the novice stage, the sooner you master these principles, the more tightly you will hug yourself. And any professional engineer will hug you, too, if they have to work with your code.
Another way of putting this is there’s a huge difference between a coder and a software engineer. A true engineer will be using this type of approach and will also be perfectly totally at home with the concept of testing.