My ideas on writing meaningful unit tests were crystallized when I read "The Practical Test Pyramid" today.
As a novice programmer, I went in mindlessly, not knowing how to test my code.
The Practical Test Pyramid is an excellent guide, but I want to emphasize unit testing today.
As the name suggests - you test a "unit" of your code.
Unit tests are beneficial in ensuring the code works as intended - it is the first step towards being confident about the logic.
Unit tests have the narrowest scope of all the tests in your test suite.
A service accesses a database to perform a set of computations; the unit tests will cover all edge cases for the performed computations. The access to the database is "assumed".
Test for all edge cases in the code.
Typically, you won't test if input deserialization works unless you've written the library itself.
When it comes to unit testing, aim for confidence coverage and not code coverage.
Loosely couple tests and code. Refactoring code is a common practice, but refactoring unit tests is stressful.
Unit tests should always answer two questions -
What is the input?
Is the output expected?
Tests, like your code, should be easy to read and understand.
Most supporters of TDD derive code from tests. Hence, a readable structure goes a long way in translating behaviour into code.
- Given an input X
- When function Y executes
- Then the outcome is Z
Ensure you maintain a delicate balance between DRY(Don't Repeat Yourself) and DAMP(Descriptive and Meaningful Phrases).
We can use various TestDoubles to replace production objects for testing purposes.
- Use stubs in cases where branching logic doesn't matter. We don't care if the database write fails.
- Use mocks when the order and the output matter. We care if the database write fails the second time.
More at Mocks aren't Stubs.
Let me know how you write unit tests in the comments!