From aa97369bc34d52abb4ef5904b80ff99dc7c2ba59 Mon Sep 17 00:00:00 2001 From: Sean Doyle Date: Sun, 21 Sep 2014 18:21:52 -0400 Subject: [PATCH] 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) ``` --- NEWS.md | 5 +++++ .../matchers/active_record/association_matcher.rb | 10 +++++++++- .../matchers/active_record/association_matcher_spec.rb | 10 ++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index c9efdd35..63e19311 100644 --- a/NEWS.md +++ b/NEWS.md @@ -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 diff --git a/lib/shoulda/matchers/active_record/association_matcher.rb b/lib/shoulda/matchers/active_record/association_matcher.rb index c410e5dc..fae3faca 100644 --- a/lib/shoulda/matchers/active_record/association_matcher.rb +++ b/lib/shoulda/matchers/active_record/association_matcher.rb @@ -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}" diff --git a/spec/shoulda/matchers/active_record/association_matcher_spec.rb b/spec/shoulda/matchers/active_record/association_matcher_spec.rb index e7a43b56..a1c49a48 100644 --- a/spec/shoulda/matchers/active_record/association_matcher_spec.rb +++ b/spec/shoulda/matchers/active_record/association_matcher_spec.rb @@ -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)