Search code examples
ruby-on-railsrubyunit-testingrspecrspec-rails

Rails/Rspec - How to use multiple it without redoing work


Multiple times I want to check multiple things over the same action, say that I want to do a PUT and see if the request returned 200 and if the response value contains some values and if the ActiveRecord model was updated.

From the next example I tried using before but it runs just before each it.

context "with valid params" do
  before do
    put my_url
  end

  it "returns a 200 response" do
    expect(response).to have_http_status(200)
  end

  it "updates the right values" do
    expect(my_model.min_area).to eq(111)
    expect(my_model.max_area).to eq(222)
  end

  it "dont update a param if it is not sent" do
    expect(my_model.budget).to eq(333)
  end
end

I want the before block to run just 1 time, and each it verify it's things. With this approach I can see clearly what failed when it fails.

Other way to do it will be removing each it and just have 1 like:

context "with valid params" do
  it "updates all" do
    put my_url

    expect(response).to have_http_status(200)
    expect(my_model.min_area).to eq(111)
    expect(my_model.max_area).to eq(222)
    expect(my_model.budget).to eq(333)
  end
end

But this approach does not tell me immediately what failed when it fails.

How can I do this without having to do the before for each it block?


Solution

  • Instead of before(:context) you might do this

    context "with valid params" do
      it "updates all" do
        put my_url
    
        expect(response).to have_http_status(200)
        expect(my_model.min_area).to eq(111)
        expect(my_model.max_area).to eq(222)
        expect(my_model.budget).to eq(333)
      end
    end
    

    But what you need is to tell RSpec to aggregate failures

    You can do it per block

    context "with valid params", :aggregate_failures do
      it "updates all" do
        put my_url
    
        expect(response).to have_http_status(200)
        expect(my_model.min_area).to eq(111)
        expect(my_model.max_area).to eq(222)
        expect(my_model.budget).to eq(333)
      end
    end
    

    Or set it globally for your project, and other options are available, choose what fancy you most.