The Six Magic Tricks
by Sandi Metz
| Origin | Query (returns something) | Command (changes something) |
|---|---|---|
| Incoming | ① ✅ Assert return value | ② ✅ Assert public side effect |
| To Self | ③ 🚫 Ignore | ④ 🚫 Ignore |
| Outgoing | ⑤ 🚫 Ignore | ⑥ ✅ Mock expectation |
Let’s decode how to test each one.
① Incoming Query Message
Example:
wheel.diameter✅ Test by checking what it returns
assert_in_delta(29, wheel.diameter, 0.01)② Incoming Command Message
Example:
gear.set_cog(27)✅ Test by checking the public side effect
gear.set_cog(27)
assert_equal 27, gear.cog③ & ⑤ Messages to Self / Outgoing Queries
-
These are internal or read-only calls.
-
🚫 Don’t test them directly.
-
They’re invisible to the outside world — so ignore them.
If your public test passes, your private method must work.
④ Private Commands (to self)
Same rule: don’t test private methods.
If you must test while debugging, keep them in a separate block with a note:
“If these tests fail — delete them.”
⑥ Outgoing Command Message
Example: Your object tells another to do something:
observer.changed(chainring, cog)✅ Use mocks/stubs to verify the message was sent
observer = Minitest::Mock.new
observer.expect(:changed, true, [52, 27])
gear = Gear.new(chainring: 52, cog: 27, observer: observer)
gear.set_cog(27)
observer.verify💡 Don’t test distant side effects (like database writes). That’s integration territory, not unit testing.