lkemitchll
lkemitchll

Reputation: 2751

Rails: Rspec and FactoryGirl Associations

I'm trying to write tests for my controllers, all was going well until I tried writing for associated models (has_many, belongs_to etc...) I've tried reading the getting started guide for FactoryGirl regarding associations and I've tried to follow as best as I can, but I get errors like:

1) GuestsController POST #create with valid attributes saves the guest to the database
 Failure/Error: post :create, guest: FactoryGirl.attributes_for(@guest)
 ArgumentError:
   Factory not registered: #<Guest:0x007fb7b1171298>

I am very new to rails and testing. Here's what I've got so far:

invites_controller:

...
has_many :guests
...

guests_controller:

....
belongs_to :invite
...

invite factory:

FactoryGirl.define do
  factory :invite do |f|
    f.name { Faker::Name.name }
    f.invite_code { Faker::Number.number(4) }

    factory :invite_with_guests do
      transient do
        guests_count 5
      end

      after(:create) do |invite, evaluator|
        create_list(:guest, evaluator.guests_count, invite: invite)
      end
   end
 end
end

guest factory:

FactoryGirl.define do 
  factory :guest do |f|
    f.name { Faker::Name.name }
    f.attendance_status "yes"
    f.starter "Mushroom"
    f.main "Beef"
    f.dessert "Chicken"
    f.dietary_requirements { Faker::Lorem.words(4) }

    f.association :invite
  end
end

guest controller:

...
  def create
    @invite = Invite.find(params[:invite_id])
    @guest = @invite.guests.create(guest_params)
    redirect_to edit_invite_path(@invite)
  end
...

guest controller spec:

...
describe "POST #create" do
  before :each do
    @invite = FactoryGirl.create(:invite_with_guests)
    @guest = @invite.guests.first
  end

  context 'with valid attributes' do
    it 'saves the guest to the database' do
      expect {
        post :create, guest: FactoryGirl.attributes_for(@guest)
      }.to change(Guest,:count).by(1)
    end
  end

  ...
end
...

All help appreciated.

EDIT:

routes:

           Prefix Verb   URI Pattern                                   Controller#Action
    invite_guests GET    /invites/:invite_id/guests(.:format)          guests#index
              POST   /invites/:invite_id/guests(.:format)          guests#create
 new_invite_guest GET    /invites/:invite_id/guests/new(.:format)      guests#new
edit_invite_guest GET    /invites/:invite_id/guests/:id/edit(.:format) guests#edit
     invite_guest GET    /invites/:invite_id/guests/:id(.:format)      guests#show
                  PATCH  /invites/:invite_id/guests/:id(.:format)      guests#update
                  PUT    /invites/:invite_id/guests/:id(.:format)      guests#update
                  DELETE /invites/:invite_id/guests/:id(.:format)      guests#destroy
   lookup_invites GET    /invites/lookup(.:format)                     invites#lookup
          invites GET    /invites(.:format)                            invites#index
                  POST   /invites(.:format)                            invites#create
       new_invite GET    /invites/new(.:format)                        invites#new
      edit_invite GET    /invites/:id/edit(.:format)                   invites#edit
           invite GET    /invites/:id(.:format)                        invites#show
                  PATCH  /invites/:id(.:format)                        invites#update
                  PUT    /invites/:id(.:format)                        invites#update
                  DELETE /invites/:id(.:format)                        invites#destroy
        dashboard GET    /dashboard(.:format)                          invites#index
             root GET    /                                             info#index

EDIT 2:

I've updated the guests controller spec as per the below possible answer, it's still failing

...
describe "POST #create" do
  before :each do
    @invite = FactoryGirl.create(:invite_with_guests)
    @guest = @invite.guests.first
  end

  context 'with valid attributes' do
    it 'saves the guest to the database' do
      expect {
        post :create, guest: @guest.attributes
      }.to change(Guest,:count).by(1)
    end

    it 'redirects the edit invite path'
  end

...
end
...

I get the following error:

http://pastebin.com/WdyT6xtK

Upvotes: 0

Views: 1276

Answers (2)

sevenseacat
sevenseacat

Reputation: 25049

You've defined your routes so that guests are nested under invites - therefore when you post guest details to create a guest, you need to also provide the invite that it's nested under, and will be associated with. See:

POST   /invites/:invite_id/guests(.:format)          guests#create

Your create action requires an invite_id as well. You're using that invite_id in the controller as well, so it must be provided.

Try:

post :create, invite_id: @invite.id, guest: @guest.attributes

Upvotes: 1

Magnuss
Magnuss

Reputation: 2310

attributes_for accepts a symbol, that returns attributes with which you can build an object.

In your case you'd need either

expect {
  post :create, guest: @guest.attributes
}.to change(Guest,:count).by(1)

Or define guest_attributes

let(:guest_attributes){ attributes_for(:guest) }
let(:guest) { create(:guest, guest_attributes) )
...
# create an invite where the first guest is your defined guest

expect {
  post :create, guest: guest_attributes
}.to change(Guest,:count).by(1)

Upvotes: 2

Related Questions