I have the following test:
test "should get create" do
sign_in(FactoryGirl.create(:user))
assert_difference('Inquery.count') do
post :create, FactoryGirl.build(:inquery)
end
assert_not_nil assigns(:inquery)
assert_response :redirect
end
and I keep getting:
2) Error:
test_should_get_create(InqueriesControllerTest):
ActiveRecord::RecordInvalid: Validation failed: Email has already been taken, License number has already been taken
What I don't understand is why I get this error in this particular test, when I have a very similar test:
test "should get create" do
sign_in(FactoryGirl.create(:user, admin: true))
assert_difference('Event.count') do
post :create, FactoryGirl.build(:event)
end
assert_not_nil assigns(:event)
assert_response :success
end
and this does just fine. The obvious difference is the admin: true line, but that has no effect as I suspected.
Added:
User_factory.rb
factory :user do
first_name "John"
last_name "Doe"
email "example#example.com"
password "foobar"
password_confirmation "foobar"
license_number '12345'
state 'AZ'
specialty 'Neurosurgery'
end
Your User is failing validations because your factory is setting up a new user for your test, but your database isn't being cleared in between. Change your factory to look like this, so that email and license_number are unique each time you create a User:
factory :user do
first_name "John"
last_name "Doe"
sequence(:email) { |n| "example#{n}#example.com" }
password "foobar"
password_confirmation "foobar"
sequence(:license_number) { |n| "12345#{n}" }
state 'AZ'
specialty 'Neurosurgery'
end
Related
I am trying to write a test for my InvitationsController#Create.
This is a POST http action.
Basically what should happen is, once the post#create is first executed, the first thing that needs to do is we need to check to see if a User exists in the system for the email passed in via params[:email] on the Post request.
I am having a hard time wrapping my head around how I do this.
I will refactor later, but first I want to get the test functionality working.
This is what I have:
describe 'POST #create' do
context 'when invited user IS an existing user' do
before :each do
#users = [
attributes_for(:user),
attributes_for(:user),
attributes_for(:user)
]
end
it 'correctly finds User record of invited user' do
post :create, { email: #users.first[:email] }
expect(response).to include(#users.first[:email])
end
end
end
This is the error I get:
1) Users::InvitationsController POST #create when invited user IS an existing user correctly finds User record of invited user
Failure/Error: post :create, { email: #users.first[:email] }
NoMethodError:
undefined method `name' for nil:NilClass
##myapp/gems/devise-3.2.4/app/controllers/devise_controller.rb:22:in 'resource_name'
# #myapp/gems/devise_invitable-1.3.6/lib/devise_invitable/controllers/helpers.rb:18:in 'authenticate_inviter!'
# #myapp/gems/devise_invitable-1.3.6/app/controllers/devise/invitations_controller.rb:67:in 'current_inviter'
# #myapp/gems/devise_invitable-1.3.6/app/controllers/devise/invitations_controller.rb:71:in 'has_invitations_left?'
I am using FactoryGirl and it works perfectly, in the sense that it returns valid data for all the data-types. The issue here is how do I get RSpec to actually test for the functionality I need.
Edit 1
Added my :user factory:
FactoryGirl.define do
factory :user do
association :family_tree
first_name { Faker::Name.first_name }
last_name { Faker::Name.last_name }
email { Faker::Internet.email }
password "password123"
password_confirmation "password123"
bio { Faker::Lorem.paragraph }
invitation_relation { Faker::Lorem.word }
# required if the Devise Confirmable module is used
confirmed_at Time.now
gender 1
end
end
It seems you're using Devise which require you to be logged in before going to the next step. On your error, Devise cannot get the same of your inviter because he's not logged.
Your test should be like this:
describe 'POST #create' do
context 'when invited user IS an existing user' do
before :each do
#users = [
attributes_for(:user),
attributes_for(:user),
attributes_for(:user)
]
#another_user = FactoryGirl.create(:user_for_login)
sign_in #another_user
end
it 'correctly finds User record of invited user' do
post :create, { email: #users.first[:email] }
expect(response).to include(#users.first[:email])
end
end
end
Example for FactoryGirl model for Devise
factory :user_for_login, class: User do |u|
u.email 'admin#myawesomeapp.com'
u.password 'password'
u.password_confirmation 'password'
u.name "MyName"
end
Of course, you need to add as much data as your validators want.. Basically for Devise you need email, password and password_confirmation. In you case, it seems you also need name.
I have a problem understanding where a specific part of code in MHartls tutorial comes from (and because of that I can't seem to change it to better fit my needs...) The part I am talking about is an rspec test:
describe "delete links" do
it { should_not have_link('delete') }
describe "as an admin user" do
let(:admin) { FactoryGirl.create(:admin) }
before do
sign_in admin
visit users_path
end
it { should have_link('delete', href: user_path(User.first)) }
it "should be able to delete another user" do
expect do
click_link('delete', match: :first)
end.to change(User, :count).by(-1)
end
it { should_not have_link('delete', href: user_path(admin)) }
end
Right now the test checks if a an admin attribute of a user is true or false. I am instead trying to check if the role attribute of a user contains the word 'Administrator'. The model is working, the views show everything correctly - but I don't know how to rewrite the test, right now it is failing.
One part that puzzles me specifically is "sign_in admin" and "user_path(admin)" - where does this come from? How does rspec know who the admin is?
And if it's an attribute that is somewhere defined, can I simply change it from admin = true/false to admin is true if role is administrator ?
Many thanks for your help!
Updated:
I do have a file called factories.rb, here is the content according to Mhartls tutorial:
FactoryGirl.define do
factory :user do
sequence(:name) { |n| "Person #{n}" }
sequence(:email) { |n| "person_#{n}#example.com"}
password "foobar"
password_confirmation "foobar"
factory :admin do
admin true
end
end
end
I tried changing it into:
FactoryGirl.define do
factory :user do
sequence(:first_name) { |n| "First #{n}" }
sequence(:last_name) { |n| "Last #{n}" }
sequence(:primary_email) { |n| "person_#{n}#example.com"}
password "foobar"
password_confirmation "foobar"
factory :role do
role "Administrator"
end
end
end
Additional information in response to your comments:
I am not using Devise - the user model is build based on the railstutorial with minor modifications (more form fields plus the admin field is in my case a field "role" with a string)
It is clear what user_path is - but why is there an (admin) behind it?
I am trying to test exactly what is in the test - the problem is that FactoryGirl tests with a user whose admin attribute is set to true.
Just to summarise:
I do not want to change what the test is testing - I only want to change how an administrator gets identified by factorygirl. Right now it tests if the attribute "admin" is set to true or false. I want it to check if the attribute "role" has the content "Administrator"
I think you may be confusing what you need to do with your test and what you need to do in your production code.
If you change the definition of FactoryGirl(:admin) as you have done, then your test is fine as is.
However, you need to change your production code so that "admin-ness" is defined in terms of role as well, and you haven't mentioned that.
I'm trying to update a record in a request spec, but it's not updating. Doing it in real life works. Here is the spec:
describe "sessions" do
before do
#user = FactoryGirl.create(:user)
#api_key = FactoryGirl.create(:api_key)
end
it "is updated properly" do
put "/api/v1/users/#{#user.id}?user_email=#{#user.email}&auth_token=#{#user.authentication_token}", {user: {name: "New Name"}},{ "HTTP_AUTHORIZATION"=>"Token token=\"#{#api_key.access_token}\"" }
#user.name.should eq("New Name")
response.status.should be(201)
end
end
The above test fails with the error:
Failure/Error: #user.name.should eq("New Name")
expected: "New Name"
got: "nil"
(compared using ==)
Name is an optional parameter, so I just don't set it in the Factory. If I do set it the line says got: "Bill" for example.
and, for completeness, here are the factories:
FactoryGirl.define do
sequence :email do |n|
"test#{n}#vitogo.com"
end
factory :user do
email
password '12345678'
password_confirmation '12345678'
goal_id 1
experience_level_id 1
gender 'Female'
factory :admin do
after(:create) { |user| user.role = 'admin'; user.save }
end
end
end
FactoryGirl.define do
factory :api_key do
access_token "MyString"
end
end
You just need to reload #user after your PUT call, e.g. #user.reload.
Here is my Spec file:
require 'spec_helper'
describe User, "references" do
it { should have_and_belong_to_many(:roles) }
it { should belong_to(:account_type) }
it { should belong_to(:primary_sport).class_name("Sport") }
it { should belong_to(:school) }
it { should belong_to(:city) }
end
describe User, "factory" do
before(:each) do
#user = FactoryGirl.create(:user)
end
it "is invalid with no email" do
#user.email = nil
#user.should_not be_valid
end
it "is valid with email" do
#user.should be_valid
end
end
Factory:
FactoryGirl.define do
factory :user do
email Faker::Internet.email
password "password"
password_confirmation "password"
agreed_to_age_requirements true
end
end
The part I am trying to "test" for and not sure how to 100% is checking to make sure when a User is created that the email address is not nil.
shoulda provides validation helpers to help you test the validations.
it { should validate_presence_of(:email) }
If you want to use rspec and write your own, then
describe User do
it "should be invalid without email" do
user = FactoryGirl.build(:user, :email => nil)
#user.should_not be_valid
#user.errors.on(:email).should == 'can't be blank' #not sure about the exact message. But you will know when you run the test
end
it "should be valid with email" do
user = FactoryGirl.build(:user, :email => "user#user.com")
#user.should be_valid
end
end
When you run the test, it would read as
User
should be invalid without email
should be valid with email
Giving a good description for your test case is very important, because it kind of acts like a documentation.
I guess the problem is that I do not know how to use factory girl with Rspec correctly. Or testing in rails correctly for that matter. Still think it is a bit weird though..
I have a class, User, with the following factory:
FactoryGirl.define do
factory :user do
name "admin"
email "admin#admin.com"
adminstatus "1"
password "foobar"
password_confirmation "foobar"
end
factory :user_no_admin, class: User do
name "user"
email "user#user.com"
adminstatus "2"
password "foobar"
password_confirmation "foobar"
end
...
My test looks like this:
...
describe "signin as admin user" do
before { visit login_path }
describe "with valid information" do
let(:user_no_admin) { FactoryGirl.create(:user_no_admin) }
let(:user) { FactoryGirl.create(:user) }
before do
fill_in "User", with: user.name
fill_in "Password", with: user.password
click_button "Login"
end
it "should list users if user is admin" do
response.should have_selector('th', content: 'Name')
response.should have_selector('td', content: user_no_admin.name)
response.should have_selector('td', content: user.name)
end
end
end#signin as admin user
...
Basically I am trying to test that if you log in as an admin, you should see a list of all the users. I have a test for logging on as a non-admin later on in the file. I have a couple of users in the db already.
In the list of users 'admin' that logged in is displayed along with the users already in the db. 'user' is however not displayed unless I do something like this before:
fill_in "User", with: user_no_admin.name
fill_in "Password", with: user_no_admin.password
It is as if it won't exist unless I use it. However, if I use a puts it does print the information I am putting, even if I do not do the 'fill_in' above.
I have a similar example where a puts helps me.
describe "should have company name" do
let(:company) { FactoryGirl.create(:company) }
let(:category) { FactoryGirl.create(:category) }
let(:company_category) { FactoryGirl.create(:company_category, company_id: company.id, category_id: category.id) }
it "should contain companies name" do
puts company_category.category_id
get 'categories/' + company.categories[0].id.to_s
response.should have_selector('h4', :content => company.name)
end
end
Without the puts above I get a
Called id for nil
Do I have to initiate(?) an object created by Factory girl before I can use it in some way?
Any other code needed?
let(:whatever)
Is not creating the objects until the first time you call them. If you want it to be available before first use, use
let!(:whatever)
instead.
Or use a before block:
before(:each) do
#company = FactoryGirl.create(:company)
....
end
Which will create the objects before you need to use them.
Instead of:
factory :user do
name "admin"
email "admin#admin.com"
...
I will do:
factory :user do |f|
f.name "admin"
f.email "admin#admin.com"
...
Instead of:
let(:user_no_admin) { FactoryGirl.create(:user_no_admin) }
let(:user) { FactoryGirl.create(:user) }
I will do:
#user_no_admin = Factory(:user_no_admin)
#user = Factory(:user)
I had a similar issue with an existing test I broke, with a slightly different cause that was interesting.
In this case, the controller under test was originally calling save, but I changed it to call save!, and updated the test accordingly.
The revised test was:
Declaring the instance a let statement
Setting an expectation on the save! method (e.g. expect_any_instance_of(MyObject).to receive(:save!) )
Using the instance for the first time after the expectation.
Internally, it would appear that FactoryGirl was calling the save! method, and after changing the expectation from save to save!, no work was actually done (and the code under test couldn't find the instance from the DB)
that I needed to update and had a hard time getting to actually pass without a hack)
Try to use trait in the factory girl,there is an example as mentioned in the this link