Search code examples
ruby-on-railsrspecmodeldevisefactory-bot

Why my factory test raises an error from another factory?


I'm quite new with Rspec and factory_bot. I have the following factories:

FactoryBot.define do
  factory :user do
    email { "[email protected]" }
    password { "azerty" }
    username { "test" }
    city { "Paris"}
  end

  factory :game do
    title { "Mega Man X" }
    nostalgia_point { 9 }
    platform { "Super Nintendo" }
    developer { "Capcom" }
    release_date { Date.new(1994,1,1) }
    game_mode { "Single player" }
    genre { ["Adventure", "Platform", "Shooter"] }
    association :owner, factory: :user
  end

  factory :favorite do
    user
    game
  end
end

When I run this test:

require 'rails_helper'

RSpec.describe Game, type: :model do
  let!(:favorite) { create(:favorite) }

  describe '::create' do
    context 'persistence' do
      it 'persists the favorite' do
        expect(Favorite.count).to eq(1)
      end
    end
  end
end

I get the following error: ActiveRecord::RecordInvalid: Validation failed: Email has already been taken, Username has already been taken

I guess it's because my user factory (devise model) is not cleaned from my other tests but I'm absolutely not sure... And I don't know if I should use something different from what I did to clean all my database. Here what I wrote in factory_bot.rb:

RSpec.configure do |config|
  config.include Warden::Test::Helpers

  config.after :each do
    Warden.test_reset!
  end

  config.include FactoryBot::Syntax::Methods
end

If there is a better practice or just tell me what I'm missing, I'll be very grateful. Thanks!


Solution

  • There must be a validation in your user model that prevents you from creating users with an email and/or username that's already in the database. And as you have a factory (favorite), that attempts to create a user and a game, without checking first if the user email and/or username already exist, it fails because it uses the values you set for them, which are always the same ("[email protected]", "test", correspondingly).

    You can use a FactoryBot sequence for that:

    sequence :email do |n|
      "test#{n}@gmail.com"
    end
    sequence :username do |n|
      "test-#{n}"
    end
    

    That prevents you from using the same value for new records as sequence yields an incremental integer, hence the value is guaranteed to not be the same as long as the test suite runs.