I have the following block of code in my User_spec.rb:
#user = { username:'newuser',
email:'new#user.com',
fname:'new',
lname:'user',
password:'userpw',
password_confirmation:'userpw'}
for creating a using using these attributes. However while I moved all these attributes to Factories.rb:
require 'factory_girl'
Factory.define :user do |u|
u.username 'newuser'
u.email 'new#user.com'
u.fname 'new'
u.lname 'user'
u.password 'newuserpw'
u.password_confirmation 'newuserpw'
end
and replace the line in user_spec.rb with:
#user = Factory(:user)
all my tests that related to the User model failed(such as tests for email, password, username etc), all were giving me
"undefined method `stringify_keys' for…"
the new user object
I had a similar problem, and it was because I was passing a FactoryGirl object to the ActiveRecord create/new method (whoops!). It looks like you are doing the same thing here.
The first/top #user you have listed is a hash of values, but the second/bottom #user is an instance of your User ojbect (built by FactoryGirl on the fly).
If you are calling something like this in your specs:
user = User.new(#user)
The first (hashed) version of #user will work, but the second (objectified) version will not work (and throw you the 'stringify_keys' error). To use the second version of #user properly, you should have this in your specs:
user = Factory(:user)
Hope that helps.
We need to see an example of a failing test to diagnose, but here is one thing that can cause it – sending an object when attributes are required. I once fixed one of my failing tests by changing:
post :create, organization: #organization
to
post :create, organization: #organization.attributes
#rowanu Answered your question, but let me layout my example too for future reference:
What was failing in my case was:
#user = User.new user_attr
#user.bookings_build(Booking.new booking_attr)
Note that I am trying to build with a booking instance and not hash of attributes
The working example:
user_attr_hash = FactoryGirl.attributes_for(:user)
booking_attr_hash = FactoryGirl.attributes_for(:booking)
#user = User.new user_attr_hash
#user.bookings.build(booking_attr_hash)
And in spec/factories/domain_factory.rb I have
FactoryGirl.define do
factory :user do
# DEFAULT USER...
password "123123123"
email "factory_girl#aaa.aaa"
# there rest of attributes set...
end
factory :booking do
start_date Date.today
end_date Date.today+3
# the rest of attributes
end
end
Related
In my rails application, a user has many baskets, and a basket belongs to a user. The User class also contains an instance method that removes the user's id from all associated baskets. I am having issues testing that method using RSpec, FactoryGirl and Faker.
class Basket
belongs_to :user, optional: true
end
class User
has_many: baskets
def disassociate_baskets
baskets.each { |b| b.update(user: nil) }
end
end
The disassociate_baskets method works with real data, my RSpec test fails because in the test environment because the basket-user association persists even after running the method. Here's my testing code:
#Basket Factory
FactoryGirl.define do
factory :basket do
date { Faker::Date.backward }
end
end
#User Factory
FactoryGirl.define do
factory :user do
name { Faker::Name.name }
end
end
#user_spec.rb
describe "When deleting purchase history" do
before do
#user = create(:user)
#user1 = create(:user)
#basket = create(:basket, user: #user)
#basket1 = create(:basket, user: #user)
#basket2 = create(:basket, user: #user1)
end
it "disassociates the user from the basket" do
#user.disassociate_baskets
expect(#basket.user).to eq nil
end
end
I've inspected the objects inside the it block, and they are fine, but #basket is still associated to #user even after running disassociate_baskets method, so my test fails. What am I missing here?
Possible solution:
expect(#basket.reload.user).to eq nil
Why you need to reload it:
When you create the new basket and assign it to #basket, it will have an associated User assigned to it. Then, you call #user.disassociate_baskets, which loops over all the baskets of that user by instantiating them one by one, and updating their user attribute.
But since #user.disassociate_baskets instantiated brand new Basket instances, the user attribute will be set to nil only in these new instances. #basket has no idea that another instance got modified, and has to be reloaded manually.
I've got a model User that has options created in a callback after it is created
# User
has_one :user_options
after_create :create_options
private
def create_options
UserOptions.create(user: self)
end
I have some simple Rspec coverage for this:
describe "new user" do
it "creates user_options after the user is created" do
user = create(:user)
user.user_options.should be_kind_of(UserOptions)
end
end
Everything worked until I added custom validation to the User model.
validate :check_whatever, if: :blah_blah
Now the spec fails and the only way to make it pass is to reload the record in the spec:
it "creates user_preferences for the user" do
user = create(:user)
user.reload
user.user_options.should be_kind_of(UserOptions)
end
What is the reason for this?
First of all I would recommend reading this article about debugging rails applications: http://nofail.de/2013/10/debugging-rails-applications-in-development/
Secondly I would propose some changes to your code:
def create_options
UserOptions.create(user: self)
end
should be
def create_options
self.user_option.create
end
that way you don't have to reload an object after save, because the object already has the new UserOptions entity in it's relation.
Assuming from the code create(:user) you are using fixtures. There might be a problem with the data that you are using the in the user.yml and the validation that you wrote, but unfortunately did not post here.
In my system, I have a user that have one company that have multiple accounts.
User sign in system using Devise, and have a virtual attribute called selected_company that was setted in CompaniesController.
I want to make multiple tests in AccountsController with this scenario.
I have this code to sign_in user, this code works well:
before :each do
#user = create(:user)
#user.confirm!
sign_in #user
end
But I must to have a specific context that I tried to code as:
context 'when user already selected a company' do
before :each do
#company = create(:company)
#account = create(:account)
#company.accounts << #account
#user.selected_company = #company
end
it "GET #index must assings #accounts with selected_company.accounts" do
get :index
expect(assigns(accounts)).to match_array [#account]
end
end
But this code won't work, when I run it I got this error:
undefined method `accounts' for nil:NilClass
My AccountsController#index have only this code:
def index
#accounts = current_user.selected_company.accounts
end
I'm new in rspec and TDD and I have some time to test everything I want, and I want to test everything to practice rspec.
I don't know if this is the best way to test this things, so I'm open to suggestions.
Replace with:
expect(assigns(:accounts)).to match_array [#accounts]
Note, :accounts instead of just account.
Also, as I see it, you don't have #accounts in your spec. Please declare that, too. :)
Probably you are not saving selected_company and when you call this on your controller it returns nil.
Try save #user.save after set selected_company:
context 'when user already selected a company' do
before :each do
#company = create(:company)
#account = create(:account)
#company.accounts << #account
#user.selected_company = #company
#user.save
end
it "GET #index must assings #accounts with selected_company.accounts" do
get :index
expect(assigns(accounts)).to match_array [#account]
end
end
Hope to help you.
Finaly, I found the problem!
I changed the before statement to:
before :each do
#company = create(:company)
#account = create(:account)
#company.accounts << #account
controller.current_user.selected_company = #company
end
And changed assigns(accounts) to assings(:accounts) (with symbol) in expect method.
I'm testing to make sure that a created user is assigned to my instance variable #user. I understand what get means, but I'm not sure what to write for the test. I'm returning with an argument error for a bad URI or URL. What's wrong with my test and how do I fix it?
it "checks #user variable assignment for creation" do
p = FactoryGirl.create(:user)
get :users
# I'm confused on what this line above means/does. What does the hash :users refer
#to
assigns[:user].should == [p]
end
The expected URI object or string error refers to get :users and the error is as follows
Failure/Error get :users
ArgumentError:
bad argument: (expected URI object or URI string)
I guess that what you want is
it "checks #user variable assignment for creation" do
p = FactoryGirl.create(:user)
get :show, id: p.id
assigns(:user).should == p
end
The line you were not sure about checks that content of the assigned variable (#user) in the show view of the user p, is equal to the p user you just created more information there
what action are you trying to test? usually, for creation, you need to test that the controller's "create" action creates a user and assigns an #user variable
I would test it this way:
describe 'POST create' do
it 'creates a user' do
params = {:user => {:name => 'xxx', :lastname => 'yyy'}}
User.should_receive(:create).with(params)
post :create
end
it 'assigns the user to an #user instance variable' do
user = mock(:user)
User.stub!(:create => user)
post :create
assigns(:user).should == user
end
end
notice that I stub/mock all user methods, since you are testing a controller you don't have to really create the user, you only test that the controller calls the desired method, the user creation is tested inside the User model spec
also, I made 2 tests (you should test only 1 thing on each it block if possible, first it test that the controller creates a user, then I test that the controller assigns the variable
I'm assuming your controller is something like this:
controller...
def create
#user = User.create(params[:user])
end
which is TOO simple, I guess you have more code and you should test that code too (validations, redirects, flash messages, etc)
I have a test that looks like this:
test "should get create" do
current_user = FactoryGirl.build(:user, email: 'not_saved_email#example.com')
assert_difference('Inquiry.count') do
post :create, FactoryGirl.build(:inquiry)
end
assert_not_nil assigns(:inquiry)
assert_response :redirect
end
That's testing this part of the controller:
def create
#inquiry = Inquiry.new(params[:inquiry])
#inquiry.user_id = current_user.id
if #inquiry.save
flash[:success] = "Inquiry Saved"
redirect_to root_path
else
render 'new'
end
end
and the factory:
FactoryGirl.define do
factory :inquiry do
product_id 2
description 'I have a question about....'
end
end
but I keep getting errors in my tests:
1) Error:
test_should_get_create(InquiriesControllerTest):
RuntimeError: Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
What am I doing wrong? I need to set the current_user, and I believe I am in the test, but obviously, that's not working.
You didn't create current_user. It was initialized only in test block.
There are two differents ways to do it:
First, use devise test helpers. Something like that
let(:curr_user) { FactoryGirl.create(:user, ...attrs...) }
sign_in curr_user
devise doc
Second, you can stub current_user method in your controllers for test env
controller.stub(current_user: FactroryGirl.create(:user, ...attrs...))
And you should use FactoryGirld.create(...) instead of FactoryGirl.build(...), because you factory objects have to be persisted.(be saved in db and has id attribute not nil)
There are several things which come to mind:
FactoryGirl.build(:user, ...) returns unsaved instance of a user. I'd suggest to use Factory.create instead of it, because with unsaved instance there's no id and there's no way for (usually session based) current_user getter to load it from database. If you're using Devise, you should "sign in" user after creating it. This includes saving record in DB and putting reference to it into session. See devise wiki
Also, passing ActiveRecord object to create action like this looks weird to me:
post :create, FactoryGirl.build(:inquiry)
Maybe there's some rails magic in play which recognizes your intent, but I'd suggest doing it explicitly:
post :create, :inquiry => FactoryGirl.build(:inquiry).attributes
or better yet, decouple it from factory (DRY and aesthetic principles in test code differ from application code):
post :create, :inquiry => {product_id: '2', description: 'I have a question about....'}
This references product with id = 2, unless your DB doesn't have FK reference constraints, product instance may need to be present in DB before action fires.