0
votes

I'm building a Rails (4.1.8) application with Postgres (0.18.3), Rspec (3.1.0), and FactoryGirl (4.5.0). I need help troubleshooting a controller test, which is throwing an Active Record::AssociationTypeMismatch error caused by a FactoryGirl object.

Here's my Fitness goals controller index action:

def index
  @fitness_goals = @member.fitness_goals.order(:start_date)    
end

This is my set-up and test of the fitness goals controller index action (fitness_goals_controller_spec.rb):

RSpec.describe FitnessGoalsController, :type => :controller do

let(:member_attributes) { { 

  "first_name" => 'Joe',
  "last_name" => 'Smith',
  "sex" => 'Male', 
  "age" => 30,
  "height" => 69,
  "weight" => 187,
  "goal" => ["Lose Fat"],
  "start_date" => Date.current    
  }
 }  

before :each do
  @request.env["devise.mapping"] = Devise.mappings[:user]
  @user = FactoryGirl.create(:user)
  sign_in @user

  @member = @user.build_member member_attributes
  @member.save

  @fitness_goal = FactoryGirl.create(:fitness_goal, member: @member)
  @fitness_goal_attributes = FactoryGirl.build(:fitness_goal).attributes
  @fitness_goal_invalid_attributes = FactoryGirl.build(:fitness_goal, timeframe_id: nil).attributes
  @fitness_goal_update_attributes = FactoryGirl.build(:fitness_goal).attributes
  @fitness_goal_update_invalid_attributes = FactoryGirl.build(:fitness_goal, timeframe_id: nil).attributes
end

describe "GET index" do
  it "assigns all fitness goals as @member.fitness_goals" do
    get :index, { :member_id => @member  }
    expect(assigns(:fitness_goals)).to eq(@member.reload.fitness_goals)
  end
end

The rspec error and backtrace:

1) FitnessGoalsController GET index assigns all fitness goals as @member.fitness_goals
 Failure/Error: @fitness_goal = FactoryGirl.create(:fitness_goal, member: @member)
 ActiveRecord::AssociationTypeMismatch:
   Target(#62887900) expected, got String(#8489780)
 # .rvm/gems/ruby-2.1.5/gems/activerecord-4.1.8/lib/active_record/associations/association.rb:216:in `raise_on_type_mismatch!'
 # .rvm/gems/ruby-2.1.5/gems/activerecord-4.1.8/lib/active_record/associations/collection_association.rb:356:in `block in replace'
 # .rvm/gems/ruby-2.1.5/gems/activerecord-4.1.8/lib/active_record/associations/collection_association.rb:356:in `each'
 # .rvm/gems/ruby-2.1.5/gems/activerecord-4.1.8/lib/active_record/associations/collection_association.rb:356:in `replace'
 # .rvm/gems/ruby-2.1.5/gems/activerecord-4.1.8/lib/active_record/associations/collection_association.rb:41:in `writer'
 # .rvm/gems/ruby-2.1.5/gems/activerecord-4.1.8/lib/active_record/associations/builder/association.rb:118:in `targets='
 # .rvm/gems/ruby-2.1.5/gems/factory_girl-4.5.0/lib/factory_girl/attribute_assigner.rb:16:in `public_send'
 # .rvm/gems/ruby-2.1.5/gems/factory_girl-4.5.0/lib/factory_girl/attribute_assigner.rb:16:in `block (2 levels) in object'
 # .rvm/gems/ruby-2.1.5/gems/factory_girl-4.5.0/lib/factory_girl/attribute_assigner.rb:15:in `each'
 # .rvm/gems/ruby-2.1.5/gems/factory_girl-4.5.0/lib/factory_girl/attribute_assigner.rb:15:in `block in object'
 # .rvm/gems/ruby-2.1.5/gems/factory_girl-4.5.0/lib/factory_girl/attribute_assigner.rb:14:in `tap'
 # .rvm/gems/ruby-2.1.5/gems/factory_girl-4.5.0/lib/factory_girl/attribute_assigner.rb:14:in `object'
 # .rvm/gems/ruby-2.1.5/gems/factory_girl-4.5.0/lib/factory_girl/evaluation.rb:12:in `object'
 # .rvm/gems/ruby-2.1.5/gems/factory_girl-4.5.0/lib/factory_girl/strategy/create.rb:9:in `result'
 # .rvm/gems/ruby-2.1.5/gems/factory_girl-4.5.0/lib/factory_girl/factory.rb:42:in `run'
 # .rvm/gems/ruby-2.1.5/gems/factory_girl-4.5.0/lib/factory_girl/factory_runner.rb:23:in `block in run'
 # .rvm/gems/ruby-2.1.5/gems/activesupport-4.1.8/lib/active_support/notifications.rb:161:in `instrument'
 # .rvm/gems/ruby-2.1.5/gems/factory_girl-4.5.0/lib/factory_girl/factory_runner.rb:22:in `run'
 # .rvm/gems/ruby-2.1.5/gems/factory_girl-4.5.0/lib/factory_girl/strategy_syntax_method_registrar.rb:20:in `block in define_singular_strategy_method'
 # ./spec/controllers/fitness_goals_controller_spec.rb:44:in `block (2 levels) in <top (required)>'

The error references the Target model, a has_and_belongs_to_many (HABTM) association with the Fitness Goal model: Target(#62887900) expected, got String(#8489780)

The relevant models:

class FitnessGoal < ActiveRecord::Base
  has_and_belongs_to_many :targets
end

class Target < ActiveRecord::Base   
  has_and_belongs_to_many :fitness_goals
end

The join table in schema:

create_table "fitness_goals_targets", id: false, force: true do |t|
  t.integer "fitness_goal_id"
  t.integer "target_id"

end

Fitness goal params:

def fitness_goal_params
  params.require(:fitness_goal).permit(:goal_list_id, :timeframe_id, :start_date, :end_date, { target_ids: [] }, { activity_ids: [] }, :notes, :member_id, :trainer_id)
end

Fitness Goal factory:

FactoryGirl.define do
  factory :fitness_goal do
association :goal_list
association :timeframe
start_date Date.current
end_date Date.current + 30              
targets ["Lose Fat", "Reduce caloric intake by x%"]
activities ["Walk x steps a day", "Climb x floors a day", "Run x miles a day"]      
association :member
association :trainer
notes 'This is a sample note.'
  end
end

What am I doing wrong? The application code works as expected in both development and production environments. It appears the problem is somewhere in my set-up for the FactoryGirl object. Implementing the HABTM association is what broke the controller test. How do I fix the issue and get the controller test passing again? Thanks for any help!

1

1 Answers

0
votes

You need to change your factory to pass in Target objects rather than strings to that association. So you need to create Target objects (or find them, if they already exist). Change

targets ["Lose Fat", "Reduce caloric intake by x%"]

to

targets { [create(:target, field: "Lose Fat"), create(:target, field: "Reduce caloric intake by x%")] }

The label I used is 'field' because I'm not sure what that field is named, so just use its name instead.