Improve failure message when `has_many` is missing

When test driving, writing an expectation such as

```ruby
it { should have_many(:children).through(:relationships).source(:child) }
```

used to provide the not-so-helpful error message

```
Failure/Error: it { should have_many(:children).through(:relationships).source(:child) }

     NoMethodError:

       undefined method `options' for nil:NilClass
```

The improved error message is

```
Expected Parent to have a has_many association called children (no association called children)
```
This commit is contained in:
Sean Doyle 2014-09-21 18:21:52 -04:00 committed by Elliot Winkler
parent 585f651460
commit aa97369bc3
3 changed files with 24 additions and 1 deletions

View File

@ -9,8 +9,13 @@
another validation on the same attribute, the matcher does not fail with an
error. ([#592])
* Fix `has_many` used with `through` so that when the association does not
exist, and the matcher fails, it does not raise an error when producing the
failure message. ([#588])
[#591]: https://github.com/thoughtbot/shoulda-matchers/pull/591
[#592]: https://github.com/thoughtbot/shoulda-matchers/pull/592
[#588]: https://github.com/thoughtbot/shoulda-matchers/pull/588
# 2.7.0

View File

@ -889,7 +889,7 @@ module Shoulda
end
def missing_options
missing_options = [missing, failing_submatchers.map(&:missing_option)]
missing_options = [missing, missing_options_for_failing_submatchers]
missing_options.flatten.compact.join(', ')
end
@ -899,6 +899,14 @@ module Shoulda
end
end
def missing_options_for_failing_submatchers
if defined?(@failing_submatchers)
@failing_submatchers.map(&:missing_option)
else
[]
end
end
def association_exists?
if reflection.nil?
@missing = "no association called #{name}"

View File

@ -325,6 +325,16 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatcher do
expect(matcher.failure_message).to match(/through relationships, but got it through conceptions/)
end
it 'produces a failure message without exception when association is missing :through option' do
define_model :child
define_model :parent
matcher = have_many(:children).through(:relationships).source(:child)
failure_message = 'Expected Parent to have a has_many association called children (no association called children)'
matcher.matches?(Parent.new)
expect(matcher.failure_message).to eq failure_message
end
it 'accepts an association with a valid :dependent option' do
expect(having_many_children(dependent: :destroy)).
to have_many(:children).dependent(:destroy)