def unlock_next
curriculum
.unlocked_for(student)
.reject(&:completed?)
end The problem
Red-green-refactor depends on a fast loop. Once the suite takes minutes, the usual substitute is to run one spec file by hand and hope it is the right one. That misses the example three directories away that also goes through the method you changed, and you find out in CI.
A recording of the examples running says which of them reach a method, which file names and
requires do not.
The fix
Suppose you change Enrollment#unlock_next. In your editor, a code lens above the method shows what
reaches it:
# ▶ reached by 4 examples · 12 app runs
def unlock_next
curriculum.unlocked_for(student)
end
On the command line:
$ ra specs_that_reach Enrollment#unlock_next
Enrollment#unlock_next
· reached by 4 examples
spec/models/enrollment_spec.rb:16
spec/models/enrollment_spec.rb:23
spec/models/enrollment_spec.rb:5
spec/requests/progress_spec.rb:4
It prints file:line locations, so its output can be passed to rspec:
$ rspec $(ra specs_that_reach Enrollment#unlock_next | grep '^spec/')
3 examples, 0 failures
Finished in 0.07 seconds
--since REF takes the methods changed in a git diff and lists the union of the examples that reach
any of them. Your agent, your editor and a pre-push hook can use it to get the examples a change
touches.
How it works
Every call tree in the recording is stamped with the spec example that produced it. That gives a map
from each example to every method it reached, and specs_that_reach reads it backwards: from a method
to every example that reached it, directly or through any depth of calls.
It lists individual examples, because a file with forty examples might have two that reach your method.
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 specs_that_reach 'Enrollment#unlock_next'
rspec $(ra specs_that_reach 'Enrollment#unlock_next' | grep '^spec/')
The method is app/models/enrollment.rb:10.
Reading the result
- The map is as fresh as the last recording. A spec written since the last recorded run is missing from it until you run that spec once.
- A request spec counts as an app run. When a request spec drives a controller, the controller
stamps its own trees with the request, so the lens counts those examples as app runs. The example
app’s
spec/requests/progress_spec.rbis one of the 12. - A new method has no examples in the map. On the first red run that is expected, because the spec you are writing is the one that will reach it.
- The map is keyed by method. A change to configuration, a YAML file or a migration can change behaviour without touching a method, so run the full suite for those changes.
Every tool reads the same recording. What gets recorded and how far to trust an answer are on the questions page.
Related tools
testing_pyramid
Boundaries crossed through doubles alone. The calls between classes that your specs make through a double alone, with how many specs stub each one and how often your recorded app traffic made it for real, plus the number of examples in each spec directory.
See more →dead_api
Find dead public API. The public methods that no caller reached in any recorded run, including those whose only caller is their own spec. Shown as a hint in your editor.
See more →split_interface
The role interfaces in a class. Groups a class's public methods by which callers used them in the recording, and lists each group as a role interface to extract.
See more →