Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
RSpec matcher for ensuring a model is accepting nested attributes for an association, and accepting/rejecting the right values.
# 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
@reject_success = model.send("#{association}").empty?
if @nested_att_present && @accept
@accept_success = ! (model.send("#{association}").empty?)
@nested_att_present && ( @reject.nil? || @reject_success ) && ( @accept.nil? || @accept_success )
failure_message_for_should do
messages = []
messages << "accept nested attributes for #{association}" unless @nested_att_present
messages << "reject values #{@reject.inspect} for association #{association}" unless @reject_success
messages << "accept values #{@accept.inspect} for association #{association}" unless @accept_success
"expected #{@model.class} to " + messages.join(", ")
description do
desc = "accept nested attributes for #{expected}"
if @reject
desc << ", but reject if attributes are #{@reject.inspect}"
chain :but_reject do |reject|
@reject = reject
chain :and_accept do |accept|
@accept = accept

hi paul,
firstly thank you for the 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?}}


pauljamesrussell commented May 10, 2012

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?


pauljamesrussell commented Jun 1, 2012

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.

Thank you!

take commented Oct 9, 2012

@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}"

+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


@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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment