Unit tests are very useful for checking how our app is working.
Otherwise, we run into all kinds of issues later on.
In this article, we’ll look at some best practices we should follow when writing JavaScript unit tests.
Unit Tests
Each unit test tests a unit of work.
It can involve multiple methods or classes that return a value or throw an exception.
They can also test state changes in the system.
We can also test 3rd party API calls.
Each test should be isolated from each other.
Any behavior should have only one test.
And they can’t affect others.
Unit Tests are Lightweight Tests
Lightweight tests are repeatable, fast, consistent, and easy to write and read.
Unit tests also code.
They should meet the same quality as the code being tested.
They can be refactored to make them more maintainable and readable.
Design Principles
Our code should be written to be testable.
We should avoid good naming conventions and comment about why we have the code.
Comments aren’t a replacement for bad design.
Also, we should avoid any repetition.
Each piece of code should have a single responsibility so that a unit test can test it.
Our code should also have a single level of abstraction in the same component.
We shouldn’t mix business logic with other technical details in the same method.
Also, our code should be configurable so that we can run our code in any environment when testing.
Also, we should adopt patterns like dependency injection to separate object creation from business logic.
Global mutable state are also hard to test and debug, so we should avoid them.
Use TDD
Test-driven development is a good way to test software components interactively.
This way, we can check the behavior of each part with each test.
The workflow is that we start with writing a failing test.
Then we make the test pass with new code changes.
Finally, we clean up the code and make sure it still works with our test.
The test first cycle makes the code design testable.
Small changes are maintainable.
And the codebase can easily be enhanced with refactoring since we can test it with tests.
It’s also cheaper to change the ode frequently and in small increments.
Tests generate confidence to add features, fix bugs, and explore new designs.
Structure Tests Properly
We should structure our tests properly.
We can nest subsets of tests.
Instead of writing:
describe('A set of functionalities', () => {
it('should do something nice', () => {
});
it('some feature should do something great', () => {
});
it('some feature should do something good', () => {
});
it('another subset of features should also do something ', () => {
});
});
We can write:
describe('A set of functionalities', () => {
it('should do something nice', () => {
});
describe('some feature', () => {
it('should do something great', () => {
});
it('should do something awesome', () => {
});
});
describe('another subset of features', () => {
it('should also do something great', () => {
});
});
});
However, don’t go overboard with nesting since it’s hard to read.
Conclusion
Unit tests are tests that test a small part of an app. We can organize them into groups.