Failing Rails 4 controller test - ActiveRecord::AssociationTypeMismatch? - postgresql

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!

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.

Related

How to setup factory_girl_rails with Rails 5.0.1

I just setup a new Rails project with Rails 5.0.1
I added factory_girl_rails to the development and test group in the Gemfile.
Then I created a article factory in the spec/factories folder:
FactoryGirl.define do
factory :article do
title { Faker::Lorem.sentence }
body { Faker::Lorem.paragraph }
end
end
my feature spec looks like this:
require 'rails_helper'
feature "guest views articles" do
let(:article1) { FactoryGirl.create(article) }
let(:article2) { FactoryGirl.create(article) }
scenario "by visiting index page" do
visit articles_path
expect(page).to have_content article1.title
expect(page).to have_content article2.title
end
end
However, when I run the spec, I get this error message:
Failures:
1) guest views articles by visiting index page
Failure/Error: let(:article1) { FactoryGirl.create(article) }
NameError:
undefined local variable or method `article' for # <RSpec::ExampleGroups::GuestViewsArticles:0x007fe957fad078>
Did you mean? article2
article1
# ./spec/features/guest_views_articles_spec.rb:5:in `block (2 levels) in <top (required)>'
# ./spec/features/guest_views_articles_spec.rb:10:in `block (2 levels) in <top (required)>'
from the error message, it seems like the article factory is not defined or recognized. Did I forgot something when setting up the article factory?
my repository is at:
https://github.com/acandael/personalsite/tree/first_test
thanks for your help,
Anthony
found the issue:
let(:article1) { FactoryGirl.create(article) }
has to be
let(:article1) { FactoryGirl.create(:article) }

Postgres foreign key violation error: insert or update on table... violates foreign key constraint

I have a web app in which I have the following relationship between two tables, a Topics and Categories table in which a Category has many Topics and a Topic belongs to a Category.
class Topic < ActiveRecord::Base
has_many :comments, dependent: :destroy
belongs_to :category
belongs_to :user
validates :subject, :body, :user_id, :category_id, presence: true
private
def self.find_by_id(params)
if params[:topic_id]
find(params[:topic_id])
else
find(params[:id])
end
end
def self.build_topic_comment(params, comment_params)
#topic = Topic.find_by_id(params)
#topic.comments.build(comment_params)
end
def self.load_comments(topic)
topic.comments.build
end
end
class Category < ActiveRecord::Base
has_many :topics, dependent: :destroy
belongs_to :user
validates :name, :user_id, presence: true
private
def self.find_by_id(params)
if params[:category_id]
find(params[:category_id])
else
find(params[:id])
end
end
def self.load_topics_desc(category)
category.topics.order(created_at: :desc)
end
def self.build_category_topic(params, topic_params)
#category = Category.find_by_id(params)
#category.topics.build(topic_params)
end
end
I added the functionality to move a topic to a different category by updating a topic's foreign key called category id. This is done trough an admin panel I coded myself and it is working correctly. When I checked the topics index page he category column is indeed updated.
The issue is that the rspec test I created is failing and giving me the following error:
Admin::TopicsController with administrator access PATCH #update with valid attributes updates a topic
Failure/Error: if #topic.update(topic_params)
ActiveRecord::InvalidForeignKey:
PG::ForeignKeyViolation: ERROR: insert or update on table "topics" violates foreign key constraint "fk_rails_d5d593e6f0"
DETAIL: Key (category_id)=(3) is not present in table "categories".
: UPDATE "topics" SET "subject" = $1, "body" = $2, "category_id" = $3, "updated_at" = $4 WHERE "topics"."id" = $5
# ./app/controllers/admin/topics_controller.rb:19:in `update'
# /Users/Beno/.rvm/gems/ruby-2.2.2/gems/devise-4.1.1/lib/devise/test_helpers.rb:19:in `block in process'
# /Users/Beno/.rvm/gems/ruby-2.2.2/gems/devise-4.1.1/lib/devise/test_helpers.rb:75:in `catch'
# /Users/Beno/.rvm/gems/ruby-2.2.2/gems/devise-4.1.1/lib/devise/test_helpers.rb:75:in `_catch_warden'
# /Users/Beno/.rvm/gems/ruby-2.2.2/gems/devise-4.1.1/lib/devise/test_helpers.rb:19:in `process'
# ./spec/controllers/admin/topics_controller_spec.rb:79:in `block (5 levels) in <top (required)>'
# ------------------
# --- Caused by: ---
# PG::ForeignKeyViolation:
# ERROR: insert or update on table "topics" violates foreign key constraint "fk_rails_d5d593e6f0"
# DETAIL: Key (category_id)=(3) is not present in table "categories".
# ./app/controllers/admin/topics_controller.rb:19:in `update'
The rspec test file:
require 'rails_helper'
RSpec.describe Admin::TopicsController, type: :controller do
describe 'with administrator access' do
let(:valid_attributes) { attributes_for(:topic) }
let(:invalid_attributes) { attributes_for(:topic, category_id: nil) }
let(:updated_attributes) { attributes_for(:topic, category_id: 3) }
before(:each) do
#topic = create(:topic)
admin = create(:admin)
sign_in admin
end
describe 'GET #index' do
it 'renders the index template' do
get :index
expect(response).to render_template(:index)
end
it 'loads all the topics in the database' do
get :index
expect(assigns(:topics)).to eq([#topic])
end
end
describe 'GET #show' do
it 'renders the show template' do
get :show, id: #topic
expect(response).to render_template(:show)
end
it 'retrieves a topic from the database' do
get :show, id: #topic
expect(assigns(:topic)).to eq(#topic)
end
it 'loads the topics comments' do
topic = create(:topic_with_comments)
get :show, id: topic
expect(topic.comments.length).to eq(5)
end
end
describe 'GET #edit' do
it 'renders the edit template' do
get :edit, id: #topic
expect(response).to render_template(:edit)
end
it 'retrieves a topic from the database' do
get :edit, id: #topic
expect(assigns(:topic)).to eq(#topic)
end
end
describe 'PATCH #update' do
context 'with valid attributes' do
it 'finds a topic in the database' do
get :edit, id: #topic
expect(assigns(:topic)).to eq(#topic)
end
it 'updates a topic' do
patch :update, id: #topic, topic: updated_attributes
#topic.reload
expect(assigns(:topic)).to eq(3)
end
it 'redirects to admin topic path' do
patch :update, id: #topic, topic: updated_attributes
expect(response).to redirect_to(admin_topics_path)
end
end
context 'with invalid attributes' do
it 'does not update a topic'
it 're-renders the edit template'
end
end
describe 'GET #new' do
it 'renders the new template'
it 'creates a new topic'
end
describe 'PUT #create' do
context 'with valid attributes' do
it 'saves a topic in the database'
it 'redirects to created topic'
end
context 'without valid attributes' do
it 'does not save a topic in the database'
it 're-renders the new template'
end
end
describe 'DESTROY #delete' do
it 'finds a topic in the database'
it 'deletes a topic from the database'
it 'redirects to admin topics index'
end
end
end
The admin topics controller:
class Admin::TopicsController < Admin::BaseController
def index
#topics = Topic.all
end
def show
#topic = Topic.find_by_id(params)
end
def edit
#topic = Topic.find_by_id(params)
#topic_options = Topic.all.collect { |topic| [ topic.category.name, topic.category_id ] }.uniq
end
def update
#topic = Topic.find_by_id(params)
if #topic.update(topic_params)
redirect_to admin_topic_path(#topic), notice: 'Topic updated successfully'
else
render :edit
end
end
private
def topic_params
params.require(:topic).permit(:subject, :body, :category_id)
end
end
Can someone point me in the right direction in regards to why the tests could be failing and the app is working correctly?
Thanks!

CanCan singleton loading with pluralized class name

I'm currently experiencing an issue with the latest version of CanCan. It seems that the gem won't load singleton resources with pluralized names.
class Preferences < ActiveRecord::Base
self.table_name = 'preferences'
belongs_to :user
end
class User < ActiveRecord::Base
has_one :preferences
end
#controller
class PreferencesController < ApplicationController
before_filter :authenticate_user! #from Devise
load_and_authorize_resource :singleton => true,
:through => :current_user,
:through_association => :preferences,
:parent => false
end
#ability.rb
class Ability
include CanCan::Ability
def initialize(user)
can [:read, :update], Preferences, :user_id => user.id
end
end
I keep getting an error like this when I run my controller spec:
Failures:
1) PreferencesController GET #show should return http success
Failure/Error: get :show
NameError:
uninitialized constant Preference
# ./spec/controllers/preferences_controller_spec.rb:10:in `block (3 levels) in <top (required)>'
It seems no matter what combination of load_resource arguments I try, I can't get my specs to pass again without reverting back to loading the resource manually like this:
class PreferencesController < ApplicationController
before_filter :authenticate_user!
def show
#preferences = current_user.preferences
authorize! :read, #preferences
end
end
Is there some magic combination of load_and_authorize_resource parameters I need to use? I have this same configuration working in another singleton controller where the resource falls under normal Rails conventions (i.e. User has_one :profile), so I know this works already under normal situations.

RailsTutorial: NoMethodError 'permanent' Rake::Test::CookieJar

app/helpers/sessions_helper.rb
module SessionsHelper
def sign_in(user)
cookies.permanent[:remember_token] = user.remember_token
self.current_user = user
end
def sign_out
self.current_user = nil
cookies.delete(:remember_token)
end
def signed_in?
!current_user.nil?
end
def current_user=(user)
#current_user = user
end
def current_user
#current_user ||= User.find_by_remember_token(cookies[:remember_token])
end
end
Tests defined in section 9.2.1 Requiring signed-in users are failing:-
At first I was getting sign_in method not found then I added
include SessionsHelper
in spec/utilities.rb file after that I started getting below error, saying no method with name permanent exists in Rake::Test::CookieJar.
Is it due to some Gem version issue.
1) User Pages edit page
Failure/Error: before { sign_in user}
NoMethodError:
undefined method `permanent' for #<Rack::Test::CookieJar:0x007ff12c661e88>
# ./app/helpers/sessions_helper.rb:3:in `sign_in'
# ./spec/requests/user_pages_spec.rb:55:in `block (3 levels) in <top (required)>'
Just ran into the same problem and got it fixed.
It seems they are not referring to the sign_in method in sessions_helper.rb but the the sign_in method in spec/support/utilities.rb
In my case this helper method in utilities.rb had a different name, after renaming it everything worked fine :-)

MMS2R and Multiple Images Rails

Here's my code:
require 'mms2r'
class IncomingMailHandler < ActionMailer::Base
##
# Receives email(s) from MMS-Email or regular email and
# uploads that content the user's photos.
# TODO: Use beanstalkd for background queueing and processing.
def receive(email)
begin
mms = MMS2R::Media.new(email)
##
# Ok to find user by email as long as activate upon registration.
# Remember to make UI option that users can opt out of registration
# and either not send emails or send them to a username+32523#example.com
# type address.
##
# Remember to get SpamAssasin
if (#user = User.find_by_email(email.from) && email.has_attachments?)
mms.media.each do |key, value|
if key.include?('image')
value.each do |file|
#user.photos.push Photo.create!(:uploaded_data => File.open(file), :title => email.subject.empty? ? "Untitled" : email.subject)
end
end
end
end
ensure
mms.purge
end
end
end
and here's my error:
/usr/local/lib/ruby/gems/1.8/gems/rails-2.3.4/lib/commands/runner.rb:48: undefined method `photos' for true:TrueClass (NoMethodError)
from /usr/home/xxx/app/models/incoming_mail_handler.rb:23:in `each'
from /usr/home/xxx/app/models/incoming_mail_handler.rb:23:in `receive'
from /usr/home/xxx/app/models/incoming_mail_handler.rb:21:in `each'
from /usr/home/xxx/app/models/incoming_mail_handler.rb:21:in `receive'
from /usr/local/lib/ruby/gems/1.8/gems/actionmailer-2.3.4/lib/action_mailer/base.rb:419:in `receive'
from (eval):1
from /usr/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `eval'
from /usr/local/lib/ruby/gems/1.8/gems/rails-2.3.4/lib/commands/runner.rb:48
from /usr/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require'
from /usr/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `require'
from /home/xxx/script/runner:3
I sent an email to the server with two image attachments. Upon receiving the email the server runs
"| ruby /xxx/script/runner 'IncomingMailHandler.receive STDIN.read'"
What is going on? What am I doing wrong?
(MMS2R docs)
Please replace
if (#user = User.find_by_email(email.from) && email.has_attachments?)
with
if ((#user = User.find_by_email(email.from)) && email.has_attachments?)