Design Tests to Fail
Tests are supposed to fail.
If a test never, ever fails in the thousands of times that you run it, that test has told you effectively nothing.
-- from Test Double - Our Thinking - Please don't mock me
This statement is very counterintuitive but I think it totally makes sense.
- If the test's purpose is to prevent the code to do the wrong thing (regression test), it is supposed to fail when the code is doing the wrong thing. (If it does not fail, then it's a false positive test.)
- If the test's purpose is to drive your design, it's supposed to fail before you write your code and guide you through the whole development cycle.
In either of these two cases, we should both design our failure messages well, so that the message can guide us:
- To find the defect faster.
- To decide what to write next faster.
Notes from that talk
- 9 abuses of mocks
- Design tests to fail
- Don't fake parts of the thing being tested
Realistic vs Mockist
Instead of treating 'realness' as a virtue to be maximized, we should clarify what our test is trying to prove and then use all of our tools, mocks included, to ensure we have sufficient experimental control
- Hard to mock code is hard to use code -> Wrap 3rd party code
- Tests are not going to make change safe
- Purposes of tests
- Test logic functions (pure functions) without mocked dependencies
- test specifies logic rules
- Test with mocked dependencies
- test specifies relationships
- Mixed levels of abstraction are bad -> Functions should do or delegate, never both
- Purposes of tests
- Don't mock intermediate data providers (when there are many layers)
- Mock direct dependencies in isolated unit tests
- Mock external system (be as real as possible) in regression tests
- If isolated unit tests won't inform your design, don't bother with
them
- Mock dependencies, but pass real values
- Don't write unit test for a system you know has design problems
- If you know it has design problems, what can unit test tell you
- Write integration/feature tests for the safety net
- Layering != Abstraction
- Make meaningfully small things
- Tools save time by reducing our thoughts and actions, but beware
when tools reduce necessary thoughts
- Many users abuse snapshot tests
- Being able to verify calls may discourage pure functions
- Only verify calls to the outside world
- Don't neglect tests' design feedback
- Mocks are there for giving you design feedback
- Hard to test code is hard to use code
- Design tests to fail
- The only good way to of mocks
- Growing Object-Oriented Software Guided by Tests
- Ease
- Confidence
- Doubt
- Outside-in Steps
- Write the code you wish you had (in your tests)
- Design messages to guide your actions
- Follow the errors
- Write the code you wish you had (in your tests)
- Benefits
- Reliable incremental progress
- Single-responsibility units
- Intention-revealing names
- Discoverable organization
- Separate values and logic
- Mostly sync pure functions
- Growing Object-Oriented Software Guided by Tests