public
Last active

RSpec matcher for ensuring a model is accepting nested attributes for an association, and accepting/rejecting the right values.

  • Download Gist
spec_helper.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
# Use: it { should accept_nested_attributes_for(:association_name).and_accept({valid_values => true}).but_reject({ :reject_if_nil => nil })}
RSpec::Matchers.define :accept_nested_attributes_for do |association|
match do |model|
@model = model
@nested_att_present = model.respond_to?("#{association}_attributes=".to_sym)
if @nested_att_present && @reject
model.send("#{association}_attributes=".to_sym,[@reject])
@reject_success = model.send("#{association}").empty?
end
if @nested_att_present && @accept
model.send("#{association}_attributes=".to_sym,[@accept])
@accept_success = ! (model.send("#{association}").empty?)
end
@nested_att_present && ( @reject.nil? || @reject_success ) && ( @accept.nil? || @accept_success )
end
failure_message_for_should do
messages = []
messages << "expected #{@model.class} to accept nested attributes for #{association}" unless @nested_att_present
messages << "expected #{@model.class} to reject values #{@reject.inspect} for association #{association}" unless @reject_success
messages << "expected #{@model.class} to accept values #{@accept.inspect} for association #{association}" unless @accept_success
messages.join(", ")
end
description do
desc = "accept nested attributes for #{expected}"
if @reject
desc << ", but reject if attributes are #{@reject.inspect}"
end
end
chain :but_reject do |reject|
@reject = reject
end
chain :and_accept do |accept|
@accept = accept
end
end

hi paul,
firstly thank you for the code...is solved my problem for the rspec, however i would like to ask how can i test for the reject_if => proc { |attrs| attrs.all? { |key, val| val.blank?}}

Hi @maheeptathgur. I think you should just be able to do:

it { should accept_nested_attributes_for(:association_name).but_reject({ :val_1 => "", :val_2 => "" }) }

Let me know if that doesn't work, and I'll try and help some more.

HI @pauljamesrussell
Thanks for the solution you gave, it worked, however i also happened to do the following and that too worked :)
it { should accept_nested_attributes_for(:association_name, :reject_if => {:val_1 => "", :val_2 => ""}) }
is it right to do this way?

Hi @maheeptathgur, no I'm afraid not - it'll just ignore the rejection test and not test for rejection. Your test will still pass, but it won't be proving what you want it to prove.

@pauljamesrussell the description message doesnt work.
When I changed it as below, it worked.

description do
  desc = "accept nested attributes for #{expected}"
  if @reject
    desc << ", but reject if attributes are #{@reject.inspect}"
  end
  desc
end

+1 to @Takehiro-Adachi . Thanks Paul for sharing!

Great, it worked. But I thought RSpec already included Shoulda macros. I'm missing something here

Thank you! Just what I was looking for

:+1:

@JohnSmall - I believe the Shoulda macros do not support the reject_if: option.

@pauljamesrussell maybe you should do a pull request to the shoulda macros to use your features for rejection?

+1 for @andreorvalho. cc @pauljamesrussell.

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.