Clean Rspec matcher for change(Model, :count).by(1)

rspec2, ruby-on-rails, ruby-on-rails-4

Solution

I do not think there is a way you can get your failing examples passing.

This is because `change` really needs a lambda, since it needs to perform your count twice (once before, and once after calling it). That's the reason I tend not to use it (or use it in context isolation).

What I usually do, instead of using the `count` matcher, is checking three things:

- The record is persisted. If I assign the model to `@model`, then I use `expect(assigns(:model)).to be_persisted`

- The record is an instance of the expected model (though might not seem useful, it is quite descriptive when using an STI). `expect(assigns(:model)).to be_a(Model)`.

- Check the last record in DB is the same as the one I just create `expect(assigns(:model)).to eq(Model.last)``

And that's the way I usually test the `change` matcher without using it. Of course, you can now create your own matcher

RSpec::Matchers.define :create_a_new do |model|
  match do |actual|
    actual.persisted? &&
      actual.instance_of?(Participant) &&
      (Participant.last == actual)
  end
end

Problem

I'm working hard trying to keep my spec files as clean as possible. Using 'shoulda' gem and writing customized matchers that follow the same pattern. My question is about creating a custom matcher that would wrap `expect{ post :create ... }.to change(Model, :count).by(1)` and could be used in the same example groups with other 'shoulda' matchers. Details bellow: Custom matcher (simplified) ``` RSpec::Matchers.define :create_a_new do |model| match do |dummy| ::RSpec::Expectations::ExpectationTarget.new(subject).to change(model, :count).by(1) end end ``` Working example ``` describe 'POST create:' do describe '(valid params)' do subject { -> { post :create, model: agency_attributes } } it { should create_a_new(Agency) } end end ``` This work OK as long as I use a `subject` lambda and my matcher is the only one in the example group. Failing examples Failing example 1 Adding more examples in the same group makes the other matcher fail because `subject` is now a lambda instead of an instance of the Controller. ``` describe 'POST create:' do describe '(valid params)' do subject { -> { post :create, model: agency_attributes } } it { should create_a_new(Agency) } it { should redirect_to(Agency.last) } end end ``` Failing example 2 The 'shoulda' matcher expect me to define a `before` block, but this become incompatible with my custom matcher ``` describe 'POST create:' do describe '(valid params)' do before { post :create, agency: agency_attributes } it { should create_a_new(Agency) } it { should redirect_to(Agency.last) } end end ``` Expected result I am looking for a way to write my custom matcher that would fit in the same example group as other matchers, meaning my custom matcher should use the `before` block to execute the controller action, the "failing example #2" above is the way I would like to write my specs. Is it possible? Thanks for reading

Original source