RSpec error User must exist with FactoryGirl - ruby-on-rails

I'm creating some test to test a controller and model. When I use FactoryGirl to create fake data I'm getting errors that the User (which the record belongs to) does not exist.
Here is my model composition.rb
class Composition < ActiveRecord::Base
belongs_to :user
belongs_to :group
validates :name, presence: true, uniqueness: {scope: :user_id}
end
Here is my FactoryGirl file composition.rb
require 'faker'
FactoryGirl.define do
factory :composition do
name { Faker::Name.name }
description { Faker::Lorem.words }
import_composition { Faker::Boolean.boolean }
import_composition_file { Faker::File.file_name('path/to') }
end
end
This is my the RSpec test that I have until this far
require 'rails_helper'
describe CompositionsController do
before(:each) do
#user = FactoryGirl.create(:user)
#group = FactoryGirl.create(:group)
sign_in #user
#composition = Composition.new(FactoryGirl.create(:composition), user_id: #user.id, group_id: #group.id)
end
describe "GET #index" do
it "renders the index template" do
get :index
expect(assigns(:composition).to eq(#composition))
expect(response).to render_template("index")
end
end
end
Right now I'm getting an error: Validation failed: User must exist, Group must exist
When I don't user FactoryGirl to create a record everything works fine.
Does any body have an suggestion why it's failing?

You don't need to pass FactoryGirl as a param to Model
#composition = FactoryGirl.create(:composition, user: #user, group: #group)
If you don't want to create the record but just want it to initialize, use build instead of create
#composition = FactoryGirl.build(:composition, user: #user, group: #group)

Related

Is it possible to pass arguments to let block during the call?

I've started to use Rspec and right now I wrote several successfully worked and pretty difficult tests. But as I need more practice I did refactor of these tests few times.
I not found an answer for my question in Rspec's documentation and that's why I here.
The question is about directive let that provides an ability to return some objects by first call and not only, you know.
My current part of rspec code is:
RSpec.describe 'Users', type: :request do
describe 'profiles' do
context 'should be visible by' do
it 'related company managers' do
company = create(:company)
sign_in create(:manager, :company, company: company) # Pay attention on this
get user_path(create(:member, :company, company: company)) # this
expect(response).to have_http_status 200
end
it 'related company owners' do
company = create(:company)
sign_in create(:owner, :company, company: company) # this
get user_path(create(:member, :company, company: company)) # and this
expect(response).to have_http_status 200
end
end
end
end
There are only two example of 63 that are under the User's spec, but they are enough. I want to refactor the code to use let which will define a method with parameters, like that:
RSpec.describe 'Users', type: :request do
let(:member) do |entity_name = :company, entity = create(entity_name)|
create :member, entity_name, entity_name => entity
end
let(:manager) do |entity_name = :company, entity = create(entity_name)|
create :manager, entity_name, entity_name => entity
end
let(:owner) do |entity_name = :company, entity = create(entity_name)|
create :owner, entity_name, entity_name => entity
end
describe 'profiles' do
context 'should be visible by' do
it 'related company managers' do
company = create(:company)
sign_in manager(:company, company) # Become more readable here
get user_path(member(:company, company)) # here
expect(response).to have_http_status 200
end
it 'related company owners' do
company = create(:company)
sign_in owner(:company, company) # here
get user_path(member(:company, company)) # and here.
expect(response).to have_http_status 200
end
end
end
end
Right after the refactor the Guard says:
1) Users profiles should be visible by related company managers
Failure/Error: sign_in manager(:company, company) # Become more readable here
ArgumentError:
wrong number of arguments (given 2, expected 0)
# ./spec/requests/users_spec.rb:38:in `block (4 levels) in <top (required)>'
From the memorized_helpers.rb of Rspec core I saw:
def let(name, &block)
# We have to pass the block directly to `define_method` to
# allow it to use method constructs like `super` and `return`.
raise "#let or #subject called without a block" if block.nil?
raise(
"#let or #subject called with a reserved name #initialize"
) if :initialize == name
MemoizedHelpers.module_for(self).__send__(:define_method, name, &block)
# Apply the memoization. The method has been defined in an ancestor
# module so we can use `super` here to get the value.
if block.arity == 1
define_method(name) { __memoized.fetch_or_store(name) { super(RSpec.current_example, &nil) } }
else
define_method(name) { __memoized.fetch_or_store(name) { super(&nil) } }
end
end
It looks like let's blocks should be defined with parameters. Or not?
I haven't enough experience to determine it and I would be glad to find it out.
I've found possible solution: return a lambda and call it in place
RSpec.describe 'Users', type: :request do
let(:member) do
->(entity_name = :company, entity = create(entity_name)) do
create :member, entity_name, entity_name => entity
end
end
let(:manager) do
->(entity_name = :company, entity = create(entity_name)) do
create :manager, entity_name, entity_name => entity
end
end
let(:owner) do
->(entity_name = :company, entity = create(entity_name)) do
create :owner, entity_name, entity_name => entity
end
end
describe 'profiles' do
context 'should be visible by' do
it 'related company managers' do
company = create(:company)
sign_in manager.(:company, company) # Pay attention on the dot here
get user_path(member.(:company, company)) # here
expect(response).to have_http_status 200
end
it 'related company owners' do
company = create(:company)
sign_in owner.(:company, company) # here
get user_path(member.(:company, company)) # and here.
expect(response).to have_http_status 200
end
end
end
end
This is not exactly that I want, but seems like.
I think you want something like:
RSpec.describe 'Users', type: :request do
describe 'profiles' do
let(:request) { get user_path(user) }
let(:user) { create(:member, :company, company: company) }
let(:company) { create(:company) }
shared_examples_for 'has visibility to user' do
before { sign_in employee }
it do
request
expect(response).to have_http_status(:success)
end
end
context 'manager' do
let(:employee) { create(:manager, company: company) }
it_behaves_like 'has visibility to user'
end
context 'owner' do
let(:employee) { create(:owner, company: company) }
it_behaves_like 'has visibility to user'
end
end
end
Similarly, you should be able to do something like:
it_behaves_like 'has visibility to user' do
let(:employee) { create(:manager, company: company) }
end
with shared examples. I suggest checking out http://www.betterspecs.org/.
(Aside, it's confusing to have a factory named :member and not :user since the GET request is for user_path; I recommend renaming it.)
The another solution is creation of helpers. For example, create a file in spec/support/users_helper.rb.
module UsersSpecHelper
def member(entity = :company)
create :member, *entity(entity)
end
def manager(entity = :company)
create :manager, *entity(entity)
end
def owner(entity = :company)
create :owner, *entity(entity)
end
private
def entity(entity)
if entity.is_a? Symbol
[entity, entity => create(entity)]
else
name = entity.class.to_s.downcase.to_sym
[name, name => entity]
end
end
end
RSpec.configure do |config|
config.include UsersSpecHelper, type: :request
end
Uncomment in spec/rails_helper.rb the line:
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
and then it can be used even better:
RSpec.describe 'Users', type: :request do
describe 'profiles' do
context 'should be visible by' do
it 'managers of related company' do
company = create(:company)
sign_in manager(company) # Changes are here
get user_path(member(company)) # here
expect(response).to have_http_status 200
end
it 'owners of related company' do
company = create(:company)
sign_in owner(company) # here
get user_path(member(company)) # and here.
expect(response).to have_http_status 200
end
end
end
end

How best to test controller variable assignment of has_many associations?

I am trying and failing to test a controller for variable assignment of the belongs_to objects. These are controller tests and there are a number of areas I could really appreciate with some help on, namely
Should I be writing such tests here and in this way.
If so how could i get it working.
Code as below:
Company.rb
class Company < ApplicationRecord
belongs_to :user
has_many :employees, inverse_of: :company
has_many :quotes, inverse_of: :company
end
Quote.rb
class Quote < ApplicationRecord
belongs_to :company
end
Employee.rb
class Employee < ApplicationRecord
belongs_to :company
end
Company has a controller with usual CRUDs, Quote has a controller with Show and Index, Employee does not have a controller. Companies#create creates all three objects and redirect_to's to Quotes#show which renders various attrs from all three models.
companies_controller.rb #create
def create
#company = current_user.companies.new(company_params)
if #company.save
#quote = #company.quotes.last
#employees = #company.employees.all
redirect_to company_quote_url(#company, #quote, #employees), notice: 'Quote request created'
else
render :new
end
end
quotess_controller.rb #show
def show
#company = Company.find(params[:company_id])
#quote = #company.quotes.find(params[:id])
#employees = #company.employees.all
end
I have a Factory Girl factory set up for eahc of the models:
Companies.rb
FactoryGirl.define do
factory :company do
sequence(:co_name) { |n| "Acme Co #{n}" }
co_number "06488522"
postcode "al1 1aa"
industry :financial_services
factory :company2 do
end
factory :company3 do
end
end
end
Quotes.rb
FactoryGirl.define do
factory :quote do
lives_overseas true
payment_frequency :monthly
factory :quote2 do
end
factory :quote3 do
end
end
end
Employees.rb
FactoryGirl.define do
factory :employee1, class: Employee do
first_name "MyString"
last_name "MyString"
email "test#test.com"
gender "MyString"
date_of_birth "2000-06-20"
salary 10000
factory :employee2 do
end
factory :employee3 do
end
end
end
And I am trying to write controller tests for Quote#show and to test the assignment of the three objects, i.e.; #company, #quote & #employees to the relataive variables. Code so far as below:
quotes_controller_spec.rb
require 'rails_helper'
RSpec.describe QuotesController, type: :controller do
let(:user) {FactoryGirl.create(:user) }
let(:company) { FactoryGirl.create(:company, user: user) }
let(:employee1) { FactoryGirl.create(:employee1, company: company) }
let(:employee2) { FactoryGirl.create(:employee2, company: company) }
let(:employee3) { FactoryGirl.create(:employee3, company: company) }
let(:quote) { FactoryGirl.create(:quote, company: company) }
describe "GET #show" do
it "returns http success" do
get :show, params: { company_id: company.id, id: quote.id, , employee_id: employee1.id }
expect(response).to have_http_status(:success)
end
it "assigns requested quote to #quote" do
get :show, params: { company_id: company.id, id: quote.id, employee1.id: employee1.id } #, employee_id: employee1.id
expect(assigns(:quote)).to eq(quote) # passes fine
expect(assigns(:company)).to eq(company) # passes fine
expect(assigns(:employee1)).to eq(employee1) # fails
end
end
end
I get an error as below:
Failures:
1) QuotesController GET #show assigns requested quote to #quote
Failure/Error: expect(assigns(:employee1)).to eq(employee1)
expected: #<Employee id: 1, first_name: "MyString", last_name: "MyString", email: "test#test.com", gender: "m",...alary: 10000, company_id: 178, created_at: "2017-07-01 11:21:27", updated_at: "2017-07-01 11:21:27">
got: nil
(compared using ==)
# ./spec/controllers/quotes_controller_spec.rb:28:in `block (3 levels) in <top (required)>'
When i run the app and use params.inspect in Quote#show template after a Company#create these are the params that are passed:
<ActionController::Parameters {"controller"=>"quotes", "action"=>"show", "company_id"=>"109", "id"=>"109", "format"=>"#<Employee::ActiveRecord_AssociationRelation:0x007fc2694a07f8>"} permitted: false>
I feel like there are a few core things I am not getting right here;
I need somehow to declare the associations within Factory Girl
My tests should somehow be testing the presence of a collection and its assignment to the #employees variable in Quotes#show, not assignment of just one employee record, which is what I'm trying, and failing, to do above.
I am unsure about whether I am crossing 'lines of separation' that perhaps ought to be present because I am testing on other model objects (Company, Quote and Employee) created in Companies#create and rendered in Quotes#show.
Any help and or guidance appreciated. The afternoon reading and googling leaves me still at a loss as to how I can get my testing strategy right here and the syntax correct for it to work properly. Incidentally all works in the app just fine, I'd just like to be able to test the assignment of the correct object in this Quotes#show method. Thanks.
The answer to part 2 of this question, re. testing assignment of a collection, in the context of my code was either:
expect(assigns(:employees)).to include(employee1)
or
expect(assigns(:employees)).to eq([employee1])
Feedback on parts 1 and 3 of this question still sought.
Thanks

How to test invalid_attribute using rspec and factorygirl

I am learning how to test on rails from this tutorial.
On one part of the tutorial, it shows how to write invalid_attribute test:
require 'rails_helper'
RSpec.describe ContactsController, type: :controller do
describe "POST #create" do
context "with valid attributes" do
it "create new contact" do
post :create, contact: attributes_for(:contact)
expect(Contact.count).to eq(1)
end
end
context "with invalid attributes" do
it "does not create new contact" do
post :create, contact: attributes_for(:invalid_contact)
expect(Contact.count).to eq(0)
end
end
end
end
I don't understand where :contact and :invalid_contact point to.
Does :contact points to Contact class? It seems like it from FactoryGirl's gh. If so, then how can I create :invalid_contact since there is no :invalid_contact class?
I have tried post :create, contact: attributes_for(:contact, :full_name => nil) but it still fails.
spec/factories/contacts.rb:
FactoryGirl.define do
factory :contact do
full_name { Faker::Name.name }
email { Faker::Internet.email }
phone_number { Faker::PhoneNumber.phone_number }
address { Faker::Address.street_address }
end
end
First test, with valid attributes pass. On model, there is presence validation validates_presence_of :full_name, :email, :phone_number, :address. What do I add in order to pass "with invalid attributes" test?
The factory will use the class with the same name. So your :contact factory will use the Contact class. You can create a new factory for the invalid contact by specifying the class to use.
factory :invalid_contact, class: Contact do
full_name nil
end
It's also possible to use traits to avoid having two different factories.
FactoryGirl.define do
factory :contact do
full_name { Faker::Name.name }
email { Faker::Internet.email }
phone_number { Faker::PhoneNumber.phone_number }
address { Faker::Address.street_address }
trait :invalid do
full_name nil
end
end
end
Then use it with attributes_for(:contact, :invalid)
The tutorial you link to says:
Following the spec above, write a spec that uses invalid attributes to
create a new contact. This spec should check that the contact is not
created.
So you need to figure out how to test for :invalid_contact using the example for :contact.
You can just add a let in your spec:
Use let to define a memoized helper method. The value will be cached
across multiple calls in the same example but not across examples.
Source: https://www.relishapp.com/rspec/rspec-core/v/3-5/docs/helper-methods/let-and-let
Then your controller spec would look like this:
...
let(:invalid_contact) { create(:contact, name: nil) }
context "with invalid attributes" do
it "does not create new contact" do
post :create, contact: attributes_for(invalid_contact)
expect(Contact.count).to eq(0)
end
end
...
this way #post action params are picked up from invalid_contact
or as #fanta suggested in comments, you can add a trait to your factory. I prefer my method because other people looking at your code will know why invalid_contact should be invalid without looking at the :contacts factory

Trouble on rspecting a FactoryGirl object

I am using Ruby on Rails 3.0.10, RSpec 2 and FactoryGirl. I have the following scenario:
In the models/user_spec.rb file I have
describe User do
let(:user) { Factory(:user) }
it "should have a 'registered' authorization do
user.authorization.should == "registered"
end
end
In the factories/user.rb file I have
FactoryGirl.define do
factory :user, :class => User do |user|
user.authorization 'registered'
end
end
In the user.rb file I have:
class User < ActiveRecord::Base
DEFAULT_AUTHORIZATION = 'registered'
validates :authorization,
:inclusion => {
:in => Authorization.all.map(&:name),
:message => "authorization is not allowed"
},
:presence => true
before_validation :fill_user_create, :on => :create
private
def fill_user_create
self.authorization = Authorization::DEFAULT_AUTHORIZATION
end
end
When I run the rspec command I get the following error:
User should have a default 'registered' Authorization
Failure/Error: let(:user) { Factory(:user) }
ActiveRecord::RecordInvalid:
Validation failed: Users authorization is not allowed
What is exactly the problem and how can I solve that?
BTW: In the models/user_spec.rb file I can use something like the following
let(:user) { User.create }
and it will work, but I prefer to use the FactoryGirl gem. What do you advice about?
Could you try modifying your spec as below and check what the results are:
it "should have a 'registered' authorization" do
system_names = Authorization.all.map(&:system_name)
system_names.should have_at_least(1).item
system_names.should include('registered')
user.authorization.should == "registered"
end

Is there a way to prevent having to specify associations in tests?

Given I have the following class
class listing > ActiveRecord::Base
attr_accessible :address
belongs_to :owner
validates :owner_id, presence: true
validates :address, presence: true
end
Is there a way I can get away with not having to keep associating an owner before I save a listing in my tests in /spec/models/listing_spec.rb, without making owner_id accessible through mass assignment?
describe Listing do
before(:each) do
#owner = Factory :owner
#valid_attr = {
address: 'An address',
}
end
it "should create a new instance given valid attributes" do
listing = Listing.new #valid_attr
listing.owner = #owner
listing.save!
end
it "should require an address" do
listing = Listing.new #valid_attr.merge(:address => "")
listing.owner = #owner
listing.should_not be_valid
end
end
No need to use factory-girl (unless you want to...):
let(:valid_attributes) { address: 'An Address', owner_id: 5}
it "creates a new instance with valid attributes" do
listing = Listing.new(valid_attributes)
listing.should be_valid
end
it "requires an address" do
listing = Listing.new(valid_attributes.except(:address))
listing.should_not be_valid
listing.errors(:address).should include("must be present")
end
it "requires an owner_id" do
listing = Listing.new(valid_attributes.except(:owner_id))
listing.should_not be_valid
listing.errors(:owner_id).should include("must be present")
end
There is if you use factory-girl
# it's probably not a good idea to use FG in the first one
it "should create a new instance given valid attributes" do
listing = Listing.new #valid_attr
listing.owner = #owner
listing.property_type = Factory(:property_type)
listing.save!
end
it "should require an address" do
# But here you can use it fine
listing = Factory.build :listing, address: ''
listing.should_not be_valid
end
it "should require a reasonable short address" do
listing = Factory.build :listing, address: 'a'*245
listing.should_not be_valid
end
I hate to be the voice of dissent here, but you shouldn't be calling save! or valid? at all in your validation spec. And 9 times out of 10, if you need to use factory girl just to check the validity of your model, something is very wrong. What you should be doing is checking for errors on each of the attributes.
A better way to write the above would be:
describe Listing do
describe "when first created" do
it { should have(1).error_on(:address) }
it { should have(1).error_on(:owner_id) }
end
end
Also, chances are you don't want to be checking for the presence of an address, you want to check that it is not nil, not an empty string, and that it is not longer than a certain length. You'll want to use validates_length_of for that.

Resources