You write the test first. Good. Your suite is the recording.
None of this needs production traffic, and none of it needs code written before its test. Runtime Analysis records your suite running on your laptop. The class you're about to write doesn't exist yet, but everything it collaborates with does, and earlier cycles' tests already ran that code for real.
Only the very first red of a new project starts from nothing. Every cycle after it adds to code that earlier cycles drove out: the collaborators you're about to stub are already there.
Record rspec locally. Every call tree is tagged with the example that produced it,
so each observation comes with the spec that made it. No staging, no production, nothing
your app depends on.
Mock Gateway in Checkout's spec and Gateway's own spec
still runs the real thing. Every object is real in its own spec, so every double has a real
recording to be checked against.
Through the cycle
Evidence at red, green and refactor.
You stub the collaborator. The stub is checked against what the real collaborator did in its own spec: the messages it received, the classes it was passed, the shape it returned.
Run the specs that reach what you just changed, and only those. The example that produced each call tree is already recorded, so the map from method to example comes free and the loop stays in seconds.
See the slice of the interface each caller actually uses, and extract the role before the design sets. Grey out the public methods nothing but their own spec ever calls.
At red
Every stub is a claim about a collaborator. We check the claim.
The one part of a new spec that makes claims about code you didn't just write is the double. The
real Gateway#charge ran in gateway_spec.rb, so the stub is held to what it
did there.
8
9
10
11
12
13
it "charges the cart" do gateway = instance_double(Gateway) allow(gateway).to receive(:charge) .and_return(:ok) ⤷ real Gateway#charge never returned a Symbol expect(checkout(gateway).call).to be_paid end
$ ra verify_mock spec/checkout_spec.rb Gateway#charge stubbed at checkout_spec.rb:10 stub returns Symbol real returns Receipt responds to #id, #amount, #paid? seen in gateway_spec.rb:14 · :22 · :31 # evidence: your suite · 0 production calls
instance_double confirms #charge exists with the right arity. It can't tell
you the real one hands back a Receipt, and Checkout will call
#paid? on whatever it gets. Green in isolation should mean green integrated.
Across the suite
Collaboration tests, matched to contract tests.
A stub in one spec is only honest if a test somewhere else shows the real object doing what the stub claims. Keeping those two sides in step by hand is the discipline that slips first. Your suite already records both.
verify_mock
Catch a lying mock. A stub that passes green but returns something the real collaborator never would. Mutation testing can't see it. The recording can.
See more →check_diff
Catch it in review. Point it at a branch or PR and it flags only what the change introduced, before it lands: a drifted mock, a fresh dependency cycle, new feature envy.
See more →dead_api
Find dead public API. Public methods no caller reached in any recorded run, including the ones whose only caller is their own spec. Greyed out in your editor.
See more →how_far_it_reached
Is your unit test a unit test? For each spec example, how many classes its real call tree touched and how deep it went. A unit spec that reaches thirty classes is telling you something.
See more →Still works. Each class is real in its own spec, so every double has something real to be checked against.
Then verify_mock has nothing to check, but the spec map, the reach per example, the
interface splits and the code only its own spec calls still land.
On a brand-new project there's nothing recorded yet. It starts paying from the second cycle, once there's a real collaborator to stub.