Reputation: 145
I'm learning about associations and I'm having trouble testing my associations using RSpec, shoulda-matchers and FactoryGirl.
I have a Game model who's instances must belong_to a manufacturer, so I'm using a presence validation.
Manufactures can have more than one game associated to them so there is a has_many games relationship from the manufacturer's point of view. Also, I want to ensure that a manufacturer is only created if it is associated with a game (through validation) (I don't want to track any manufacturer that does not have any games associated with it).
Here is the code:
class Game < ActiveRecord::Base
belongs_to :manufacturer
validates :name, presence: {message: 'The game name is required'}
validates :description, presence: {message: 'A short game description is required'}
validates :manufacturer, presence: true
end
class Manufacturer < ActiveRecord::Base
has_many :games
validates :name, presence: {message: 'The manufacturer name is required and must be unique'}
validates :name, uniqueness: {message: 'A manufacturer by this name already exits.'}, if: "name.present?"
validates :games, presence: { message: 'Every manufacturer must be associated with at least one game.' }
end
So as you can see both validate each other, so there will be no games without manufacturer and no manufacturers without games.
I'm having a hard time trying to create a Factory using FactoryGirl to create a valid manufacturer instance for testing.
How can I create the manufacturer factory and the games factory in order to have a valid Factory where I can test the associations using shoulda-matchers? (the DB is properly set up with the columns for foreign keys and ids)
EDIT
I have tried the following without luck...
TRY 1
FactoryGirl.define do
factory :game do
name "Space Invaders"
description "Space Invaders was one of the early blockbuster shooting games."
release_date '1978'
association :manufacturer, strategy: :build
end
end
FactoryGirl.define do
factory :manufacturer do
name 'Atari'
games {create_list(:game, 1)}
end
end
Error:
/Users/mymac/.rvm/gems/ruby-2.1.5@sr-arcade-nomad/bin/ruby_executable_hooks:15: stack level too deep (SystemStackError)
TRY 2
FactoryGirl.define do
factory :game do
name 'Space Invaders'
description "Space Invaders was one of the early blockbuster shooting games."
manufacturer 'Atari'
end
end
FactoryGirl.define do
factory :manufacturer do
name 'Atari'
games {create_list(:game, 1)}
end
end
Error:
.../gems/activerecord-4.1.1/lib/active_record/associations/association.rb:216:in `raise_on_type_mismatch!': Manufacturer(#70244044131760) expected, got String(#70244001467180) (ActiveRecord::AssociationTypeMismatch)
Upvotes: 1
Views: 846
Reputation: 191
I'm looking for a solution with which I can easily override the games association. If that's no concern in this case, you can use an after build hook:
factory :manufacturer do
…
after(:build) do |m|
m.games << build(:game, manufacturer: m)
end
end
The problem with overriding is "kind of solved" if you put that after(:build) in a trait, but then build(:manufacturer)
is no longer valid. So still no ideal but nevertheless a working factory.
Then again, this situation with circular presence validation is not ideal to start with. The only way in Rails to do this (the Rails way) is with a nested form. You will then create nested objects with params like { name: … , games_attributes: {1 => {<attributes for game 1}, …}
. You probably should build the factory like with games_attributes
too then. Other cases are just not possible:
game = Game.new …
manufacturer = Manufacturer.new …
Now save them (nested) with those validations in place …, one will have to go first …
Upvotes: 0
Reputation: 664
Try the following code:
factory :game do
name
description
manufactor
end
factory :manufacturer do
name
games { create_list(:game, 1) }
end
Upvotes: 0