testing relationships in rspec, rails - ruby-on-rails

I am trying to write a test to check that an associated object exists and the relevant id is stored in the database.
A panel belongs to a page. A page has many panels.
A sample of the current errors I have is this
CorporatePanel
Failure/Error: #instance = corporate_panel
ActiveRecord::RecordInvalid:
Validation failed: Corporate page does not exist, Corporate page can't be blank
# ./spec/support/project_helpers.rb:11:in `block (2 levels) in setup_factories'
# ./spec/models/corporate_panel_spec.rb:8:in `block (2 levels) in <top (required)>'
# -e:1:in `<main>'
Which I believe is caused because my test isnt creating an instance of page before a panel is created.
In my panel model I use this
validate :check_associates
private
def check_associates
associated_object_exists CorporatePage, :corporate_page_id
end
The method 'associated_model_exists' is a helper
def associated_object_exists(klass,fn,required=true)
id=self.send(fn)
id_setter=(fn.to_s+'=').to_sym
unless klass.find_by_id(id)
self.send(id_setter,nil)
id=nil
end
if required==true and id.nil?
errors.add(fn,"does not exist")
return false
end
end
The Panel test looks like this,
setup_factories
before do
#instance = corporate_panel
end
it { should belong_to(:corporate_page) }
mandatory_integer :section
mandatory_belongs_to CorporatePage
The madatory belongs_to looks like this
def mandatory_belongs_to(model)
context "#{model}" do
context "where the id is incorrect" do
before do
allow(model).to receive(:find_by_id).and_return(nil)
end
it "should be invalid" do
expect(#instance).to_not be_valid
end
end
context "where the id is correct" do
before do
allow(model).to receive(:find_by_id).and_return(true)
end
it "should be valid" do
expect(#instance).to be_valid
end
end
end
end
Update
FactoryGirl.define do
factory :corporate_panel do
corporate_page_id 1
section 1
# position 1
panel_type "MyString"
title "MyString"
headline "MyString"
body "MyText"
workflow_state "MyString"
end
end
FactoryGirl.define do
factory :corporate_page do
title "MyString"
static_descriptor "Home"
# workflow_state "MyString"
end
end
How would I ensure that a page is created before the model, if that is even the problem?

The problem is the way you've defined your factory. It should create the appropriate association while creating the corporate panel. It should be something like this:
FactoryGirl.define do
factory :corporate_page do
title "MyString"
static_descriptor "Home"
# workflow_state "MyString"
end
end
FactoryGirl.define do
factory :corporate_panel do
corporate_page # this refers to the factory defined above!! and will create appropriate association.
section 1
# position 1
panel_type "MyString"
title "MyString"
headline "MyString"
body "MyText"
workflow_state "MyString"
end
end
Here are some links for further reading on factories: http://www.hiringthing.com/2012/08/17/rails-testing-factory-girl.html and https://github.com/brennovich/cheat-ruby-sheets/blob/master/factory_bot.md

You can use an association as recommended in Surya's answer. If you need more control over the creation of factories, you can also explicitly pass in the Corporate page when creating the CorporatePanel:
let(:corporate_page) { FactoryGirl.create :corporate_page }
let(:corporate_panel) { FactoryGirl.build :corporate_panel, corporate_page: corporate_page }
This might be preferable if you want to use the :corporate_panel factory in other examples without an associated corporate_page.

Related

uninitialized constant Wing

When I am trying to run test case for model using Rspec and Factory girl it is showing uninitialized constant Wing
code in factories/wing.rb
FactoryGirl.define do
factory :wing do
wing_name "Example Title"
is_deleted "0"
mg_school_id "1"
created_by "2013-06-02 02:28:12"
updated_by "2013-06-02 02:28:12"
end
end
code in model/mg_wing_spec.rb
require 'rails_helper'
RSpec.describe MgWing, type: :model do
it "has a valid factory" do
#hai=FactoryGirl.create(:wing)
end
end
Change filename factories/wing.rb to factories/mg_wing.rb
Your factory name should be the same in your model and rspec filename.
Here are a couple of tutorials if you want it:
https://medium.com/#JonoYeong/setting-up-rspec-and-factory-girl-8cf287801099
https://semaphoreci.com/community/tutorials/working-effectively-with-data-factories-using-factorygirl
You either need the factory name same as the model name or pass the class param and factorygirl will take care of it
Solution 1
FactoryGirl.define do
factory :mg_wing do
...
end
end
Solution 2
FactoryGirl.define do
factory :wing, class: MgWing do
...
end
end

Using factory girl to create HABTM association

I've been trying now for hours to get factorygirl to create two factories - one for users, one for organizations.
But I don't seem to understand how I can reflect a 'has_and_belongs_to_many' relationship in factories, as soon as I try to create an organization and associate it with an admin user, I run into various error messages (depending on the approach I use).
My model seems to be working fine, my seed file populates the dev DB and all the associations are created.
Right now my files look like this:
user factory
FactoryGirl.define do
factory :user do
email 'example#example.com'
password 'password'
password_confirmation 'password'
after(:create) {|user| user.add_role(:user)}
factory :owner do
after(:create) {|user| user.add_role(:owner)}
end
factory :admin do
after(:create) {|user| user.add_role(:admin)}
end
factory :superadmin do
after(:create) {|user| user.add_role(:superadmin)}
end
end
end
Organization factory
FactoryGirl.define do
factory :organization do |f|
f.name "example"
f.website "www.aquarterit.com"
f.association :users, :factory => :admin
end
end
in my specs I test this:
describe Organization do
it "has a valid factory" do
FactoryGirl.create(:organization).should be_valid
end
it "is invalid without a name" do
FactoryGirl.build(:organization, name: nil).should_not be_valid
end
it "is associated with at least one admin user" do
FactoryGirl.create(:organization)
it { should have_and_belong_to_many(:users)}
end
end
all three tests are failing, here are the error message:
1) Organization has a valid factory
Failure/Error: FactoryGirl.create(:organization).should be_valid
NoMethodError:
undefined method `each' for #<User:0x007fadbefda688>
# ./spec/models/organization_spec.rb:7:in `block (2 levels) in <top (required)>'
2) Organization is invalid without a name
Failure/Error: FactoryGirl.build(:organization, name: nil).should_not be_valid
NoMethodError:
undefined method `each' for #<User:0x007fadc29406c0>
# ./spec/models/organization_spec.rb:11:in `block (2 levels) in <top (required)>'
3) Organization is associated with at least one admin user
Failure/Error: organization = FactoryGirl.create(:organization)
NoMethodError:
undefined method `each' for #<User:0x007fadc2a3bf20>
# ./spec/models/organization_spec.rb:15:in `block (2 levels) in <top (required)>'
Any help is as always very much appreciated!
Update
In theory the same thing that works for assigning roles to the user should work for assigning an admin to the organization. But if I change organizations.rb to
FactoryGirl.define do
factory :organization do
name "example"
website "www.aquarterit.com"
after(:create) {|organization| organization.add_user(:admin)}
end
end
I get following error (I do have gem shoulda installed):
1) Organization is associated with at least one admin user
Failure/Error: it { should have_and_belong_to_many(:users)}
NoMethodError:
undefined method `it' for #<RSpec::Core::ExampleGroup::Nested_1:0x007ff2395f9000>
# ./spec/models/organization_spec.rb:16:in `block (2 levels) in <top (required)>'
Looks like you are not assigning users correctly and not creating the :admin user properly. For this to work, you need to assign an array of users to organization.users. And, you need to populate that array with a User instance (this assumes you have a User factory named :admin).
factory :organization do
name "example"
website "www.aquarterit.com"
after(:create) {|organization| organization.users = [create(:admin)]}
end
I do it this way, questions and tests have a HABTM relationship so:
FactoryGirl.define do
factory :question do
question 'Some stupid question'
user nil
factory :question_with_test do
# factory_girl's dynamic attributes, ignore it and pass it to evaluator
transient do
test nil
end
after(:create) do |question, evaluator|
create(:question_tests, question: question, test: evaluator.test)
end
end
end
end
Now I can create a question with HABTM to the Test model:
let(:test) { FactoryGirl.create :test, user: user }
let(:question_test_1) { FactoryGirl.create :question_with_test, user: user, test: test }
The question_tests factory is very basic:
FactoryGirl.define do
factory :question_tests, class: 'QuestionTest' do
question
test
end
end

Factory will create one object with a role, but not two

I am using rolify with a User model and a Task model (Rails 4). One of the roles a user can have is "owner." I want to use Factory Girl to create a user object and assign it a role. Here is my factory:
FactoryGirl.define do
factory :task do
owner "Steve"
agency "an agency"
facility "a facility"
description "This task must absolutely be done"
due_date "2013-12-22 03:57:37"
completed_date "2013-12-22 03:57:37"
factory :task_with_owner do
ignore do
user_id nil
end
after(:create) do |task, user_id|
User.find(user_id).add_role :owner, task
end
end
end
end
This spec passes:
it 'is capable of creating a valid object with owner' do
#user = create(:user)
task = create(:task_with_owner, user_id: #user.id)
expect(#user.has_role? :owner, task).to be_true
end
This spec fails:
it 'is capable of creating two valid objects with an owner' do
#user = create(:user, username: 'janeblow')
task = create(:task_with_owner, user_id: #user.id)
expect(#user.has_role? :owner, task).to be_true
task = create(:task_with_owner, user_id: #user.id)
expect(#user.has_role? :owner, task).to be_true
end
The error is:
Failure/Error: task = create(:task_with_owner, user_id: #user.id)
ActiveRecord::RecordNotFound:
Couldn't find User with id=#<#<Class:0x000000050f5e10>:0x00000004c9ed08>
# ./spec/factories/tasks.rb:19:in `block (4 levels) in <top (required)>'
# ./spec/models/role_spec.rb:15:in `block (2 levels) in <top (required)>'
Why?
Your after(:create) block looks a little wrong. Try changing it to the following:
after(:create) do |task, vars|
User.find(vars.user_id).add_role :owner, task
end
Then re-run your failing spec.
Because you told the factory to ignore the user_id being passed in and to use nil instead, in your after(:create) block you have to access it from the passed in attributes (in the second block argument, vars in this case). You were almost there, but were passing the object factory_girl uses to hold the attributes rather than the attribute itself from within that object.
See the Transient Attributes section here for another example - https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md

Factory Girl: uninitialized constant

I have a factory such as:
FactoryGirl.define do
factory :page do
title 'Fake Title For Page'
end
end
And a test:
describe "LandingPages" do
it "should load the landing page with the correct data" do
page = FactoryGirl.create(:page)
visit page_path(page)
end
end
My spec_helper.rb contains:
require 'factory_girl_rails'
and yet I keep getting:
LandingPages should load the landing page with the correct data
Failure/Error: page = FactoryGirl.create(:page)
NameError:
uninitialized constant Page
# ./spec/features/landing_pages_spec.rb:5:in `block (2 levels) in <top (required)>'
This is a new project, so I don't believe the test is actually the problem. I believe it could be setup incorrectly. Any ideas on things to try and/or where to look to resolve this?
My uneventful pages.rb file:
class Pages < ActiveRecord::Base
# attr_accessible :title, :body
end
It looks from your file names like the model is actually named LandingPage. The factory is trying to guess your class name based on the name you have given it. So :page becomes Page.
You can change name of the factory or you can add an explicit class option:
FactoryGirl.define do
factory :landing_page do
title 'Fake Title For Page'
end
end
or
FactoryGirl.define do
factory :page, :class => LandingPage do
title 'Fake Title For Page'
end
end
Looks like the name of your model is plural: Pages. This should really be singular: Page. You'll need to rename the file to app/models/page.rb as well. FactoryGirl is assuming a singular model name.

Rspec + OAuth: Having trouble signing in from within a test (omniauth mock)

I'm testing a controller in my app which should render one view for users who've given over their info and requested access ("signed in guests") and another view for the rest of the world (anonymous users). I'm using OmniAuth and Rolify.
Test goes like this:
context 'signed in guests' do
before do
guest = create(:user,:guest)
OmniAuth.config.mock_auth[:google] = {
uid: guest.uid
}
session[:user_id] = guest.id
end
it "shows the Please Wait for Confirmation view" do
puts "Rspec says Current User: #{#current_user.inspect}"
get :index
response.should render_template :pending_authorization
end
end
User factory looks like this:
FactoryGirl.define do
factory :user do
name "MyString"
email "MyString"
provider "MyString"
uid "MyString"
trait :admin do
after(:create) {|user| user.add_role(:admin)}
end
trait :guest do
after(:create) {|user| user.add_role(:guest)}
end
trait :authorized_user do
after(:create) {|user| user.add_role(:user)}
end
end
end
However, when I run this, my puts actually gives me this:
Rspec says Current User: nil
So... why is the current user nil even though I create it with the omniauth mock?
Edit: current_user comes from my application helper, if that helps any.
Thanks in advance!

Resources