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 another validation on the same attribute, the matcher does not fail with an
error. ([#592]) 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 [#591]: https://github.com/thoughtbot/shoulda-matchers/pull/591
[#592]: https://github.com/thoughtbot/shoulda-matchers/pull/592 [#592]: https://github.com/thoughtbot/shoulda-matchers/pull/592
[#588]: https://github.com/thoughtbot/shoulda-matchers/pull/588
# 2.7.0 # 2.7.0

View File

@ -889,7 +889,7 @@ module Shoulda
end end
def missing_options def missing_options
missing_options = [missing, failing_submatchers.map(&:missing_option)] missing_options = [missing, missing_options_for_failing_submatchers]
missing_options.flatten.compact.join(', ') missing_options.flatten.compact.join(', ')
end end
@ -899,6 +899,14 @@ module Shoulda
end end
end end
def missing_options_for_failing_submatchers
if defined?(@failing_submatchers)
@failing_submatchers.map(&:missing_option)
else
[]
end
end
def association_exists? def association_exists?
if reflection.nil? if reflection.nil?
@missing = "no association called #{name}" @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/) expect(matcher.failure_message).to match(/through relationships, but got it through conceptions/)
end 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 it 'accepts an association with a valid :dependent option' do
expect(having_many_children(dependent: :destroy)). expect(having_many_children(dependent: :destroy)).
to have_many(:children).dependent(:destroy) to have_many(:children).dependent(:destroy)