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

I can't create object with FactoryGirl.create, validation failed User has already been taken


I am trying to create a balance object with the gem FactoryGirl, but when I open rails console and write command FactoryGirl.create(:balance) I get an error.

I create associations has_one and belong_to between my models, in addition add validations for unique keys in foreign fields, these are my models.

Balance

class Balance < ActiveRecord::Base
  #association macros
  belongs_to :user
  belongs_to :wallet

  #validation macros
  validates :user_id, presence: true, uniqueness: true
  validates :wallet_id, presence: true, uniqueness: true
end

Wallet

class Wallet < ActiveRecord::Base
  #association macros
  belongs_to :user
  belongs_to :wallet_type
  has_one :bank_account, dependent: :destroy
  has_one :debit_card, dependent: :destroy
  has_one :credit_card, dependent: :destroy
  has_one :balance, dependent: :destroy

  #validation macros
  validates :user_id, presence: true
  validates :wallet_type_id, presence: true

end

User

class User < ActiveRecord::Base
  #association macros  
  ...
  has_one :salt
  has_one :balance

end

Factory Balance

FactoryGirl.define do
  factory :balance do
    user
    wallet
    amount 2000
  end
end

My test in Balance

RSpec.describe @Balance, type: :model do

  describe "Balance" do

    before(:each) do
      @user = FactoryGirl.create(:user)
      @wallet = FactoryGirl.create(:wallet)
      @balance = FactoryGirl.create(:balance, user_id: @user[:id], wallet_id: @wallet[:id])
    end

    it "when is valid" do
      expect(@balance).to be_valid
    end

    it "when is presence user_id" do
      expect(@balance).to validate_presence_of :user_id
    end

    it "when is presence wallet_id" do
      expect(@balance).to validate_presence_of :wallet_id
    end

    pending "when is uniqueness user_id" do
      expect(@balance).to validate_uniqueness_of :user_id
    end

    pending "when is uniqueness wallet_id" do
      expect(@balance).to validate_uniqueness_of :wallet_id
    end

    it "when is belongs_to :user" do
      should belong_to(:user)
    end

    it "when is belongs_to :wallet" do
      should belong_to(:wallet)
    end

  end

end

When I create balance object FactoryGirl.create(:balance) I get the following error.

ActiveRecord::RecordInvalid: Validation failed: User has already been taken
    from .../lib/active_record/validations.rb:79:in `raise_record_invalid'
    from .../lib/active_record/validations.rb:43:in `save!'
    from .../lib/active_record/attribute_methods/dirty.rb:29:in `save!'
    from .../lib/active_record/transactions.rb:291:in `block in save!'
    from .../lib/active_record/transactions.rb:351:in `block in with_transaction_returning_status'
    from .../lib/active_record/connection_adapters/abstract/database_statements.rb:213:in `block in transaction'
    from .../lib/active_record/connection_adapters/abstract/transaction.rb:184:in `within_new_transaction'
    from .../lib/active_record/connection_adapters/abstract/database_statements.rb:213:in `transaction'
    from .../lib/active_record/transactions.rb:220:in `transaction'
    from .../lib/active_record/transactions.rb:348:in `with_transaction_returning_status'
    from .../lib/active_record/transactions.rb:291:in `save!'
    from .../lib/factory_girl/configuration.rb:14:in `block in initialize'
    from .../lib/factory_girl/evaluation.rb:15:in `[]'
    from .../lib/factory_girl/evaluation.rb:15:in `create'
    from .../lib/factory_girl/strategy/create.rb:12:in `block in result'
    from .../lib/factory_girl/strategy/create.rb:9:in `tap'
    from .../lib/factory_girl/strategy/create.rb:9:in `result'
    from .../lib/factory_girl/factory.rb:42:in `run'
    from .../lib/factory_girl/factory_runner.rb:23:in `block in run'
    from .../lib/active_support/notifications.rb:166:in `instrument'
    from .../lib/factory_girl/factory_runner.rb:22:in `run'
    from .../lib/factory_girl/strategy_syntax_method_registrar.rb:20:in `block in define_singular_strategy_method'
    from (irb):1
    from .../lib/rails/commands/console.rb:110:in `start'
    from .../lib/rails/commands/console.rb:9:in `start'
    from .../lib/rails/commands/commands_tasks.rb:68:in `console'
    from .../lib/rails/commands/commands_tasks.rb:39:in `run_command!'
    from ..lib/rails/commands.rb:17:in `<top (required)>'
    from bin/rails:4:in `require'
    from bin/rails:4:in `<main>'irb(main):002:0> a= FactoryGirl.create(:balance)

Solution

  • Problem

    You see this error when you run the test:

    ActiveRecord::RecordInvalid: Validation failed: User has already been taken
    

    because you have created that user in any of your previous test run. So, it's getting that validation error.

    Solution

    You can use database_cleaner gem which will ensure a clean state of the test database against which you run your test.

    You can install the gem by adding this to your Gemfile:

    group :test do
      gem 'database_cleaner'
    end
    

    And, then run:

    bundle install
    

    After that, configure database_cleaner in your spec_helper.rb:

    RSpec.configure do |config|
      config.before(:suite) do
        DatabaseCleaner.strategy = :transaction
        DatabaseCleaner.clean_with(:truncation)
      end
    
      config.before(:each) do
        DatabaseCleaner.start
      end
    
      config.after(:each) do
        DatabaseCleaner.clean
      end
    end
    

    This configuration will make sure the data from the previous test run will be truncated after each spec. And, every time you run a new spec, you do so against a clean database.

    You can see how to configure database_cleaner with RSpec from the gem's documentation page.