def unlock_next
curriculum
.unlocked_for(student)
.select(&:unlocked?)
.map(&:id)
end #unlocked?#id · across 214 deploys of your app Book is the one
that appears in your suite, and the other two were found in pooled production traffic. The counts in
this picture illustrate pooled production traffic, which needs the hosted graph. The output below is
from the example app.
The problem
Suppose verify_mock has reported one of your stubs as a false positive. The consumers of
Enrollment#unlock_next call #unlocked? and #title on each thing Curriculum#unlocked_for
returns, and your double returns Symbols:
curriculum = instance_double(Curriculum)
allow(curriculum).to receive(:unlocked_for).and_return([:geometry])
You now know the shape the consumers need: an object that answers #unlocked? and #title. You
still have to find a real class with that shape, and the app may contain a lighter class that plays
the same role. No code declares the role, so you cannot look it up.
Grep finds the classes that define unlocked?. Its results include classes whose unlocked? means
something else and classes that nothing has passed to Enrollment.
The fix
ra find_implementers takes the set of messages the call site needs:
$ ra find_implementers '#unlocked?, #title'
real types responding to {#unlocked?, #title}
· 1 class
Book 4 630 outside traced code (a factory or seeds)
One class answered both messages on the same instance: Book, 630 times in the recorded traffic.
No spec body called it directly. It was built outside traced code, by the factory in specs and by
the seeds in traffic, so there is no recorded construction path to quote. Use it in the spec:
book = create(:book, title: "Geometry")
allow(curriculum).to receive(:unlocked_for).and_return([book])
expect(enrollment.unlock_next).to eq([book])
Or keep a double and build it from the recorded contract, instance_double(Book, unlocked?: true, title: "Geometry"), which verify_mock then passes.
How it works
- For a call site, or for a stub that was reported as a false positive, the recording gives the
messages the code sends to a value: here
#unlocked?and#title. - The tool groups every recorded receiver by object identity within each call tree and keeps the classes whose instances received every message in the set.
- Classes seen more often rank higher, and each count is split into spec and app-run counts. When a traced constructor built an instance, the row includes the shortest recorded path that constructed one.
Today it searches the recording on your machine. Pooling the search across every deploy’s traffic, which would include classes no spec mentions, needs the hosted graph and is on the roadmap.
Run it on your app
The output on this page comes from our example Rails app, and it is regenerated whenever a tool
changes. These are the commands behind it. On your own app, run ra record first, then put your
own class, method or file in their place.
ra find_implementers '#unlocked?, #title'
The messages are sent from app/controllers/progress_controller.rb:4 and :6.
Reading the result
- The match is on message names.
#unlocked?on aDooris the same message as#unlocked?on aBook, so check where each class was seen before you use it. - A construction path is shown when a traced constructor built the object. An ActiveRecord model or an object built by a factory has no recorded constructor, and its row says so in place of a path.
Every tool reads the same recording. What gets recorded and how far to trust an answer are on the questions page.
Related tools
verify_mock
Stubs that disagree with the real object. Compares what each stub in a spec returns with what the real method returned in the recording, and reports the stubs that return a class the real method did not.
See more →observed_shape
The interface a call depends on. The messages your code sends to a value, read from recorded runs. They are the interface a call depends on, whether or not anyone declared it.
See more →who_actually_answered
The classes that answered a call. At a duck-typed call site, the concrete classes that received the message in recorded runs, and how often each did. In an editor it answers go to implementation from the recording.
See more →