Closed pirj closed 3 years ago
Let's wait for a response on https://github.com/rspec/rspec-expectations/issues/1287 ?
rspec-expectations
bug has been confirmed, and it's most probably an easy fix.
I think I have to come up with a different argumentation, but memoized helpers are rspec-core
's concept, while it is arguably misused in the matcher part of the expectation which is rspec-expectations
's concept. They don't overlap much and don't use each other's syntax in their examples.
The code that initially triggered me was:
let(:expected_value) { {a: 1, b: 2, c: 3} }
it 'returns a proper hash' do
expect(parser.parse).to eq expected_value
end
It felt extremely wrong to use memoized helpers in the "right-hand" side of the expectation. Yes, it's possible, and you won't shoot yourself in the leg, and even if you do, it's not critical, just the failure message is off.
With so many options, around, the use of memorized helpers has no reasonable justification in my book.
Namely,
expected_value = {a: 1, b: 2, c: 3}
it 'returns a proper hash' do expect(parser.parse).to eq expected_value end
Beware, the lifecycle of such variables is completely different from `let`, see https://github.com/rubocop-hq/rubocop-rspec/issues/991
2. Method
```ruby
def expected_value
{a: 1, b: 2, c: 3}
end
it 'returns a proper hash' do
expect(parser.parse).to eq expected_value
end
An additional bonus: methods are flexible, i.e. you can parameterize the expected_value
by passing something to it. This is achieved with several let
s, but is subjectively harder:
def expected_value(last: 3)
{a: 1, b: 2, c: last}
end
it 'returns a proper hash' do
expect(parser.parse("a1b2c3")).to eq expected_value
expect(parser.parse("a1b2c4")).to eq expected_value(last: 4)
end
I find it hard to achieve the same with let
s without using several context
example groups.
Additionally, helper methods do have access to memoized helpers.
def go_well
eq(easy)
end
let(:easy) { 'ha!' }
it { is_expected.to go_well }
So, unfortunately, except for the initial argument that there's a slight bug in rspec-expectation
, I can only provide to use arguably more efficient testing styles. No real argumentation of why it is inherently bad and should be avoided at all costs.
I've prototyped a cop to enforce this, but it turned out to have a lot of false positives in real-world-rspec
that I didn't foresee. https://github.com/rubocop/rubocop-rspec/commit/b7ebdb9362e86f7310c56058334d210a544445d8
Closing for now, because:
let(:expected_value) { {a: 1, b: 2, c: 3} }
seems to be my personal view on clean specs
Reusage may have undesired side effects, e.g. (see https://github.com/rspec/rspec-expectations/issues/1287):
will output for the second failure the same
extra
as for the first one:instead of
https://github.com/rspec/rspec-expectations/blob/bd10f0cf3970932781efcd09b5e427877d16a6c2/lib/rspec/matchers/composable.rb#L113
https://github.com/rspec/rspec-expectations/pull/518
The change is best viewed as https://github.com/rubocop-hq/rspec-style-guide/blob/discourage-using-memoized-matchers/README.adoc#matcher-reuse