Search code examples
ruby-on-railsruby-on-rails-4rspeccapybararspec-rails

RSpec get around unique requirement in testing


My Signup database has an index on email with a unique requirement. This is great, but the problem is that I'm trying to run integration tests, and every time I go rake rspec spec/features/...rb, unless I did rake db:test:purge, rake db:test:prepare first, it runs into the unique problem and refuses to run. How do I streamline this?

From the code below, you can see that every time I'm running the test, I'm creating a set of seed data with my before(:all), but since the seed data is always the same, this is driving the uniqueness error.

I'm happy to just put this seed data elsewhere or create it some other way, as long as my test suite is still able to run using this seed data.

describe "how requests should flow" do

    before(:all) do 
        @signup_dd = Signup.create(email:"[email protected]")
    end

    it "should have 2 inventories and 2 signups to start" do
        Signup.count.should == 1
    end

    describe "request creation" do
        before do
            Signup.find_by_id(@signup_dd)
            visit '/requests/new'
            save_and_open_page
            fill_in '#borrow__1', :with => 1
            click_button
        end
        it "should affect new Requests and Borrows" do
            ...
        end
    end
end

Solution

  • There are two ways to fix this:

    1. Remove the (:all) from the before block. RSpec will execute the before block for each test. It will then undo itself after each test. This is really what you want as it ensures changes made by each test do not bleed into other tests. This is usually the recommended approach.

    2. Keep the (:all), but then add a (:after) block to undo your changes. With the :all argument, the before block is only executed once instead of every time. However, it doesn't automatically undo itself like :each, so the :after block becomes necessary. It is up to you, however, to figure out what needs to go in there. In your example, for instance, it might be:

      after(:all) do
          Signup.delete_all # Or whatever undoes what the before block did
      end
      

    See this blog post regarding the use of the two techniques.