13

Possible Duplicate:
Is it possible for RSpec to expect change in two tables?

it "should create a new Recipe" do
  expect { click_button submit }.to change(Recipe, :count).by(1)
end

This allows me to check that the 'Recipe' model has one more entry, but I'd like to also check that the 'Ingredient' model has one more entry. The expect block can only be executed once, since the form is submitted already.

I know I could just make another 'it' block, but I feel that there must be a DRYer way.

Community
  • 1
  • 1
David Frey
  • 198
  • 1
  • 1
  • 10

2 Answers2

51

I would propose DRYing it up by redefining the test subject (and using stabby lambdas for fun):

describe "recipe creation" do
  subject { -> { click_button submit } }
  it { should change(Recipe, :count).by(1) }
  it { should change(Ingredient, :count).by(1) }
end

Update: Although it may look less DRY, these days I would probably still keep on using the expect syntax, since it's recommended and I'm generally moving away from should, but perhaps make some minor changes for spec readability:

describe "recipe creation" do
  let(:creating_a_recipe) { -> { click_button submit } }

  it "changes the Recipe count" do
    expect(creating_a_recipe).to change(Recipe, :count).by(1)
  end

  it "changes the Ingredient count" do
    expect(creating_a_recipe).to change(Ingredient, :count).by(1)
  end
end

Note: you may see in the RSpec documentation for the change matcher that expect uses curly brackets. This is, of course, correct, but the reason that standard parenthesis work in this example is that the code that changes mutable state (contained in creating_a_recipe) is in a lambda that gets called when passed into expect as a parameter.

Regardless, in this case either expect(creating_a_recipe) or expect { creating_a_recipe } can be used successfully, and whichever one you use is up to personal preference.

Eben Geer
  • 3,696
  • 3
  • 31
  • 34
Paul Fioravanti
  • 16,423
  • 7
  • 71
  • 122
0

You could abstract all of that into a helper method

def test_creation_of(model)
  it "should create a new #{model}" do
    expect { click_button submit }.to change(model.constantize, :count).by(1)
  end
end

But I would only recommend it if you will do this for many models. Else it will just make the code harder to read. If you do, best to put it into a spec helper.

Also, based on other precedents in your tests, you could pass a Const object instead of a string (as I used in the example above).

then

it "should create a new Recipe" do
  test_creation_of( 'Recipe' )
  test_creation_of( 'Ingredient' )
end
Fábio Batista
  • 25,002
  • 3
  • 56
  • 68
New Alexandria
  • 6,951
  • 4
  • 57
  • 77
  • for the future: this code works. Downvotes is generally inappropriate in this situation - you should comment with disagreement instead (or show why this code can't solve the problem). – New Alexandria Dec 20 '12 at 14:29
  • This is a nice abstraction for testing creation of a model, but the OP was asking about how to test for the creation of two models with one click. You're right though, it fosters a better community to write comments rather than simply downvoting. My bad. – Ben Taitelbaum Dec 22 '12 at 18:09
  • Interestingly, because it's been 2 days, SO isn't letting me take back my downvote until you update the answer. – Ben Taitelbaum Dec 22 '12 at 18:10
  • Thanks! This will still click submit twice, btw, so may not work (for example, if the page redirects after clicking submit, or the form is no longer valid). – Ben Taitelbaum Dec 22 '12 at 21:14