Search code examples
ruby-on-railsrubyrspecruby-on-rails-5rspec-rails

RSpec ActiveRecord::RecordInvalid: Validation failed: Email has already been taken despite having sequence in FactoryBot


I have a model called "availabilities" in my Rails application that allows vendors to set their availability (i.e. their work hours). Thus, availabilities belong_to vendors and belong_to users, and a user has_many vendors and a vendor has_many availabilities.

I've been trying to create Rspec tests for my availability#destroy action. The specific test I refer to is:

#spec/controllers/availabilities_controller_spec.rb
require 'rails_helper'

RSpec.describe AvailabilitiesController, type: :controller do

  describe "availabilities#destroy action" do

    it "should allow a user who created the availability to destroy it"
      availability = FactoryBot.create(:availability) 
      sign_in availability.user
      delete :destroy, params: { id: availability.id, vendor_id: availability.vendor_id}
      availability = Availability.find_by_id(availability.id)
      expect(availability).to eq nil
   end 
  end
end

When I run this test however, I receive the following error:

"An error occurred while loading ./spec/controllers/availabilities_controller_spec.rb. Failure/Error: user = FactoryBot.create(:user)

ActiveRecord::RecordInvalid: Validation failed: Email has already been taken"

However, I use factory bot for my factories, and I have my user factory to run as a sequence (see below):

FactoryBot.define do
  factory :user do
    sequence :email do |n|
      "dummyEmail#{n}@gmail.com"
    end
    password "secretPassword"
    password_confirmation "secretPassword"
    confirmed_at Time.now
  end
end

How can the email already be taken? What could explain this error?


Solution

  • I recommend you to use Faker along with FactoryBot. It will give your more flexibility and eliminate the need to do this sequence trick. Faker generates fake data with ease.

    In any way, use database_cleaner to clean your test environment database after each test. You'll only need to set this is up:

    # ./spec/rails_helper.rb
    
    # start by truncating all the tables but then use the faster transaction strategy the rest of the time.
    config.before(:suite) do
      DatabaseCleaner.clean_with(:truncation)
      DatabaseCleaner.strategy = :transaction
    end
    
    # start the transaction strategy as examples are run
    config.around(:each) do |example|
      DatabaseCleaner.cleaning do
        example.run
      end
    end