ruby-on-railsrspecrspec-rails

RSpec test to expect only certain properties of an ActiveRecord model to change


I am successfully testing whether certain properties of an ActiveRecord model are updated. I also want to test that ONLY those properties have changed. I was hoping I could hook into the model's .changes or .previous_changes methods to verify the properties I expect to change are the only ones being changed.

UPDATE

Looking for something equivalent to the following (which doesn't work):

it "only changes specific properties" do
  model.do_change
  expect(model.changed - ["name", "age", "address"]).to eq([])
end

Solution

  • Try something like this

    expect { model.method_that_changes_attributes }
      .to change(model, :attribute_one).from(nil).to(1)
      .and change(model, :attribute_two)
    

    If the changes are not attributes, but relations you might need to reload the model:

    # Assuming that model has_one :foo
    expect { model.method_that_changes_relation }
      .to change { model.reload.foo.id }.from(1).to(5)
    

    EDIT:

    After some clarification from the OP comment:

    You can do this then

    # Assuming, that :foo and :bar can be changed, and rest can not
    
    (described_class.attribute_names - %w[foo bar]).each |attribute|
      specify "does not change #{attribute}" do
        expect { model.method_that_changes_attributes }
          .not_to change(model, attribute.to_sym)
        end
      end
    end
    

    This is essentially what you need.

    This solution has one issue though: it will call method_that_changes_attributes for each attribute, and this can be inefficient. If that's the case - you may want to make your own matcher that accepts an array of methods. Start here