stephenmurdoch
stephenmurdoch

Reputation: 34603

Rails and Rspec - checking for existence of errors when form submission fails

I have a Foo model, where :name is required on creation.

I'm writing a spec to test the validations

it 'should not create an invalid Foo' do
  fill_in "Name", :with=>""
  # an error message will be displayed when this button is clicked
  click_button "Create Foo"
end

How can I confirm that the error message is present on the page?

I've tried page.errors.should have_key(:name) but this is not right.

I guess I could do page.should have_content("Name can't be blank") but I'd prefer to avoid coupling my integration tests so strongly with the content

Upvotes: 6

Views: 9636

Answers (3)

mhriess
mhriess

Reputation: 396

If you're testing your validations correctly at the unit testing level, adding another test for your desired error message is easy:

describe Foo do
  describe "validations" do
    describe "name" do
      before { @foo = FactoryGirl.build(:foo) } # or Foo.new if you aren't using FactoryGirl

      context "when blank" do
        before { @foo.name = "" }

        it "is invalid" do
          @foo.should_not be_valid
        end

        it "adds the correct error message" do
          @foo.valid?
          @foo.errors.messages[:name].should include("Name cannot be blank")
        end
      end # positive test case omitted for brevity
    end
  end
end

This way, you've isolated the error generation and copy to the model, and it's reliably tested, which allows you to implement some kind of global error display (for instance, using flash[:error], without having to test each error message explicitly at the view level.

Upvotes: 12

Jesse Wolgamott
Jesse Wolgamott

Reputation: 40277

You said that you're writing a spec to test the validations, but I see that you're testing in capybara (or similar) with "fill_in"

Instead, I highly recommend writing unit tests to test your models.

spec/models/your_model_spec.rb

require 'spec_helper'
describe YourModel do

  it "should not allow a blank name" do
    subject.name = ""
    subject.should_not be_valid
    subject.should have(1).error_on(:name)
  end

end

That way, you're testing in isolation --- only what you need to test, not whether the controller is working correctly, or the view, or even looping through the flash.

This way, your test is fast, durable, and isolated.

Upvotes: 6

Dty
Dty

Reputation: 12273

Your it block says 'should not create an invalid Foo' and if that's really what you're testing do a unit test on the model Foo rather than an integration test.

However, if you're testing for the SPECIFIC error message on the page then you do need to check for the content. To make the test less coupled you could check for a specific html element. For example, my errors show up in a flash message. I check for them by looking for a div with class named error or alert. I ignore the actual message and just check to make sure SOME sort of error is showing up.

Upvotes: 1

Related Questions