And they pin behavior more than strict correctness, in my experience, but that’s a valuable signal. Canaries in the mine.
The problem is that the behavior they pin is not the official API-exposed behavior. The behavior they pin is mostly implementation details. If diffuse(bomb) is supposed to diffuse the bomb, I need to test that given a legal instance of a Bomb it'd end up diffused. A unit test cannot test that - if diffuse is the unit being tested, then bomb is not allowed to be an actual bomb - it has to be a mock. And thus diffuse(bomb) does not test that diffuse diffuses a bomb - it tests that diffuse cuts very specific set of wires in a very specific order.
- If the bomb change, the unit test is till green because the mock is still replaying the old behavior.
- If I fix
diffuseto handle the new bomb, the unit test becomes red.
Once the bomb change, the unit test shows the opposite of what a test is supposed to show - and now you have to fix the test itself. Because that's what unit tests test - they test themselves. They don't test the system.
An integration test that would bring in an actual bomb, diffuse it, and check if it's diffused in a controlled environment where it can't do damage by blowing up - is going to be more expensive to run, yes, but at least you will be getting a meaningful signal out of it. It'd be testing something of value. The unit test is cheap to run (although writing it was probably more expensive), but it gives you nothing. It's completely pointless.