ruby-lsp-rspec
ruby-lsp-rspec copied to clipboard
Resolving spec DSL references
One of the most challenging things about working with Rspec tests that have been written in a subject-let-fixture style is tracking down the definitions of subjects and lets -- especially when the test file gets pretty long. This is a process that computers are better at than human brains. I feel like it would be hugely beneficial to offer something akin to go-to-definition for subjects and lets within a spec-formatted ruby file.
As someone who works on this project and ruby-lsp, how achievable would something like this be via a ruby-lsp addon? It seems the add-on would need its own index, similar to RubyIndexer, capable of interpreting and building an index from a spec DSL.
tracking down the definitions of subjects and lets
Can you give me a few examples? Like do you mean when you cmd+click a subject call, it brings you to the associated subject { ... } block?
If that's the case, then it's pretty difficult to do it 100% accurate atm because subject could be:
- a local variable
- a method
- a RSpec subject
And for let it's even harder as it could be inherited from outer scopes.
Maybe we can use the document highlight feature to highlight potential subject(:foo) or let(:foo) when pointing at foo?
Thanks for taking the time to review and respond @st0012 !
Here's a simplified example of what I'm talking about. It's not great testing code, but it seems fairly typical for an rspec suite.
describe "#can_make_reservation?" do
let(:user) { User.new(active: true) }
subject { Reserver.new(user) }
context "when the user is active" do
it "is true" do
expect(subject.can_make_reservation?).to eq true
end
end
context "when the user is inactive" do
let(:user) { User.new(active: false) }
it "is false" do
expect(subject.can_make_reservation?).to eq false
end
end
end
Could we help the user track down the subject/let fixtures for a given example?
The first example's ("#can_make_reservation? when user is active is true") text fixtures are the subject defined on line 3 and the let(:user) defined on line 2.
The second example's ("#can_make_reservation? when user is inactive is false") test fixtures are the subject defined on line 3 and the let(:user) defined on line 12, because user has been overridden within the nested context.
This is mental work that has to be done every time a human tries to maintain an rspec suite. It's work that becomes significantly more burdensome as the length and nesting-depth of an rspec test file grows.
Somehow what example you're interested in has to be established, otherwise the resolution wouldn't work correctly.
A naive go-to-method would work fine for the first example ("#can_make_reservation? when user is active is true"). The user could command-click subject on line 7 and be brought to the subject definition on line 3. Then, they could command-click user within the subject's block on line 3 and be brought to the let(:user) definition on line 2.
But for the second example ("#can_make_reservation? when user is inactive is false"), we'd need a different strategy that would respect the overridden let(:user) from line 12. So maybe a simple go-to-method doesn't work -- or at least it would need to be biased to the example the user is interested in.
Could there be an editor command to "print example fixtures" could be run for a given example? Using it for first example, would print out:
03: subject { Reserver.new(user) }
02: let(:user) { User.new(active: true) }
Using it for the second example would print out:
03: subject { Reserver.new(user) }
12: let(:user) { User.new(active: false) }
Does this help? Thank you again for taking the time to review and respond.
I tried to prototype something today, but it's not possible without some addon API changes from ruby-lsp itself, which I proposed in https://github.com/Shopify/ruby-lsp/issues/2191.
@st0012 Nice! Thank you for looking into this!