Many developers hate having to write tests. Besides literally not knowing how to do it, the reason is often a test suite full of anti-patterns that is not fun to work with.
Here is one of the dozens of testing anti-patterns I identified over the years:
🛑 Test Anti-pattern 🛑
Adding more than 3 expectations to a test case
More than three expectations in a test case is a strong indicator that multiple test cases are mixed together in one test.
❌ avoid:
it('returns false when password is invalid', () => {
expect(isValidPassword('password')).toBe(false)
expect(isValidPassword('UpCumQRA')).toBe(false)
expect(isValidPassword('emn@pg*zyUMs')).toBe(false) // no number
expect(isValidPassword('2ybgxuNeTm')).toBe(false) // no special char
})
All test cases deserve their own test with a proper name.
âś… prefer:
it('rejects password without number', () => {
expect(isValidPassword('P@ssword')).toBe(false)
})
it('rejects password without special character', () => {
expect(isValidPassword('Passw0rd')).toBe(false)
})
it('rejects password without uppercase letter', () => {
expect(isValidPassword('p@ssw0rd')).toBe(false)
})
it('rejects password without lowercase letter', () => {
expect(isValidPassword('P@SSW0RD')).toBe(false)
})
Tests that mix multiple test cases in one test, like in the bad example above, often carry implicit knowledge, and it will be difficult and expensive to maintain these tests.
Notice also how the lousy code example above relies on comments to explain itself. Cleaner code can eliminate the need for comments in most cases. In the improved example, the tests take a double role. They verify the correctness of the code, and they also document the code.
Distinct test cases will make it also easier to find bugs when something goes wrong.
By the way, if you are working with Jest, there is also a linting rule that can remind you and your team to identify, refactor, and avoid tests with more than three expectations.
See you around,
– David