-
-
Notifications
You must be signed in to change notification settings - Fork 912
Description
Description
We can't expect for the presence of an option with value false if it's against a modified value of a Rails default.
Reproduction Steps
By default, the some options such as touch are set as false by the framework. shoulda-matchers supports having expectations about these default values:
# app/models/author.rb
class Author < ApplicationRecord
has_many :articles
end# spec/models/author_spec.rb
RSpec.describe Author do
it { is_expected.to have_many(:articles).touch(false) }
# => this expectation passes
endMy first point is that there are arguments not to provide such an expectation from shoulda-matchers. The default value for touch is a framework feature, not a business code one. In the same way it is considered bad practice to test frawework features in business code specs, the expectation in the previous example is testing that the framework is adding implicitly the value touch: false to associations.
My second point is that that the current way shoulda-matchers deals with boolean values can mess with explicit false values.
We recently created the PR #1607 (in code review as I write this issue) to support a strict_loading option for associations.
strict_loading can be enabled by default at the configuration level with the following instruction:
# config/application.rb
# or, config/environments/<environment>.rb
config.active_record.strict_loading_by_default = true
# In current versions of Rails, this is set to false by defaultAs business code, a developer could decide to have every single association with a strict_loading constraint, except one:
# app/models/author.rb
class Author < ApplicationRecord
has_many :articles, strict_loading: false
endIt would make sense, in that case, to cover this exception:
# spec/models/author_spec.rb
RSpec.describe Author do
it 'rejects an association missing explicit strict_loading option to false with the correct message' do
message = [
'Expected Author to have a has_many association called articles ',
'(articles should have strict_loading set to false)',
].join
expect {
expect(described_class.new).
to have_many(:articles).
strict_loading(false) # strict_loading matcher is yet to be approved.
}.to fail_with_message(message)
end
endIn the current state, the expectation of the last example would not pass if the strict_loading option was missing from the model's association declaration, because of how shoulda-matchers deals with boolean casting and validation.
In lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb #type_cast, booleans are casted with !!value.
This means a missing value for strict_loading is considered as a false value.
I understand that changing this behaviour would be a breaking change, as many current users of the gem have probably introduced spec expectations based on Rails default values.
This issue is a first step: discussing the situation and figure out what we want to do with it.
If it happens that we agree on a breaking change, I'm happy to work on the deprecation process and change itself.
Expected behavior
# app/models/author.rb
class Author < ApplicationRecord
has_many :articles, strict_loading: false
end# spec/models/author_spec.rb
RSpec.describe Author do
it 'rejects an association missing explicit strict_loading option to false with the correct message' do
message = [
'Expected Author to have a has_many association called articles ',
'(articles should have strict_loading set to false)',
].join
expect {
expect(described_class.new).
to have_many(:articles).
strict_loading(false) # strict_loading matcher is yet to be approved.
}.to fail_with_message(message)
end
endExpected: Test passes, matcher fails with message.
Actual behavior
Test fails, the matcher is valid and no failing message is raised.
System configuration
shoulda_matchers version: 6.1.0
rails version: 7.1.3
ruby version: 3.3.0