I have two Factory Girl objects on a single file.
spec/factories/users.rb
FactoryGirl.define do
factory :user do
first_name "Charlie"
last_name "Brown"
email "email#example.com"
password "charbar1234"
password_confirmation "charbar1234"
end
factory :admin do
first_name "Bob"
last_name "Marley"
email "bob#marley.com"
password "bobmarley1234"
password_confirmation "bobmarley1234"
admin true
end
end
When I call create(:user), my test runs fine. When I call create(:admin), I get the following error...
Failures:
1) admin accesses the database
Failure/Error: create(:admin)
NameError:
uninitialized constant Admin
# ./spec/features/admins/admin_spec.rb:5:in `block (2 levels) in <top (required)>'
Here is the test..
spec/features/admins/admin_spec.rb
require "rails_helper"
describe "admin" do
it 'accesses the database' do
create(:admin)
visit root_path
click_link "Log In"
fill_in "Email", with: "bob#marley.com"
fill_in "Password", with: "bobmarley1234"
click_button "Log In"
expect(current_path).to eq(admin_dashboard_path)
with 'h1' do
expect(page).to have_content 'Administration'
end
expect(page).to have_content 'Manage Users'
expect(page).to have_content 'Manage Articles'
end
end
You need to put the factory :admin inside of the factory :user.
It should look something like this:
FactoryGirl.define do
factory :user do
...
factory :admin do
admin true
end
end
end
See Factorygirl Admin Creation
Related
i'm new at rails and i'm testing my devise gem User with capybara rspec and factory girl.
Here's spec code:
require 'rails_helper'
RSpec.describe User, type: :request do
it "displays the user's email after successful login" do
user = FactoryGirl.create(:user, email: 'test#test.com', password: 'password')
visit root_url
fill_in 'Email', with: 'test#test.com'
fill_in 'Password', with: 'password'
click_button 'Log in'
expect page.has_content?('jdoe')
end
end
The problem is in
expect page.has_content?('jdoe')
No matter what i put instead 'jdoe' test works perfectly without any errors.
Here's factory:
FactoryGirl.define do
factory :user do
email 'test#test.com'
password 'password'
password_confirmation 'password'
end
end
Maybe I missed something or going with a wrong way. How should I test here?
# spec/features/sessions_spec.rb
require 'rails_helper'
RSpec.feature "Sessions" do
scenario "displays the user's email after successful login" do
user = FactoryGirl.create(:user)
visit root_url
fill_in 'Email', with: user.email
fill_in 'Password', with: user.password
click_button 'Log in'
expect(page).to have_content("Signed in successfully")
# will give a false postitive if form is rerended?
expect(page).to have_content(user.email)
end
end
A few things here:
Use a feature and not a request spec for end to end testing.
Don't hardcode object attributes. The whole point of factories is that the factory takes care of generating unique data so that the tests don't give false positives due to residual state.
Use expect(page).to instead of expect page.should. expect(page).to sets a subject and uses a RSpec matcher which will show you the text of the page in an error message if it does not match.
I'm following Michael Hartl's great book on Ruby on Rails(rails 3.2 version). I'm having some problems in section 9.3.3. Pagination.
After I modified my factory at spec/factories.rb using sequence like this(note that I have the previous version commented out):
FactoryGirl.define do
#factory :user do
# name "Michael Hartl"
# email "michael#example.com"
# password "foobar"
# password_confirmation "foobar"
#end
factory :user do
sequence(:user){ |n| "Person #{n}" }
sequence(:email){ |n| "person_#{n}#example.com"}
password "foobar"
password_confirmation "foobar"
end
end
I cannot use this anymore in my tests:
let(:user){ FactoryGirl.create(:user) }
For instance the test with code:
require 'spec_helper'
describe "User pages" do
subject { page }
describe "index" do
let(:user){ FactoryGirl.create(:user) }
before(:all){ 30.times {FactoryGirl.create(:user) }}
after(:all) {User.delete_all}
before(:each) do
valid_signin user
visit users_path
end
it { should have_selector('title', text: 'All users') }
it { should have_selector('h1', text: 'All users') }
.
.
.
end
Returns errors such as:
Failure/Error: before(:all){ 30.times {FactoryGirl.create(:user) }}
NoMethodError:
undefined method `user=' for #<User:0x00000002760140>
# ./spec/requests/user_pages_spec.rb:16:in `block (4 levels) in <top (required)>'
# ./spec/requests/user_pages_spec.rb:16:in `times'
# ./spec/requests/user_pages_spec.rb:16:in `block (3 levels) in <top (required)>'
It somehow makes sense that I can't use this syntax (let(:user){...}) anymore, since I'm now creating sequences of elements, but I can't seem to find a fix for this.
Any ideas? thanks!
I think you mean sequence(:name) instead of sequence(:user) in your factory. It's looking for a method user= in your User model instead of the name= method.
Here is my request spec:
require 'spec_helper'
describe "user signs in", focus: true do
before(:each) do
create(:account_type)
end
it "allows users to sign in with their email address and password" do
test_user = build_stubbed(:user)
visit root_path
click_link "Sign-In"
fill_in "user_email", with: test_user.email
fill_in "user_password", with: test_user.password
click_button "Sign In"
page.should have_content "Vitals"
end
end
The issue is that I can see the request happen to log the user in, but when the user is logged in, the request is redirected back to the home page without an error message.
Here is my user factory:
FactoryGirl.define do
factory :user do
email Faker::Internet.email
password "password"
password_confirmation "password"
agreed_to_age_requirements true
subscriptions {[create(:subscription)]}
end
end
subscriptions factory:
FactoryGirl.define do
factory :subscription do
account_type_id 1
trial_expiry 30.days.from_now
active true
end
end
I am not sure why the request is failing and not signing-in the user.
I'm using minitest with factory girl and capybara for integration tests. Capybara works fine when I don't user factory girl to create a user object, like this:
it "logs in a user successfully" do
visit signup_path
fill_in "Email", :with => "joey#ramones.com"
fill_in "Password", :with => "rockawaybeach"
fill_in "Password confirmation", :with => "rockawaybeach"
click_button "Create User"
current_path == "/"
page.text.must_include "Signed up!"
visit login_path
fill_in "Email", :with => "joey#ramones.com"
fill_in "Password", :with => "rockawaybeach"
check "Remember me"
click_button "Log in"
current_path == "/dashboard"
page.text.must_include "Logged in!"
page.text.must_include "Your Dashboard"
end
But as soon as I try to create a user with factory girl weird things start happening, such as the visit method and click_button methods stop working. For instance, there doesn't seem to be anything wrong with this test:
require "test_helper"
describe "Password resets" do
before(:each) do
#user = FactoryGirl.create(:user)
end
it "emails user when requesting password reset" do
visit login_path
click_link "password"
fill_in "Email", :with => user.email
click_button "Reset my password"
end
end
And here's my factories.rb:
FactoryGirl.define do
factory :user do |f|
f.sequence(:email) { |n| "foo#{n}#example.com" }
f.password "secret"
f.password_confirmation "secret"
end
end
Here's the actual error that I'm getting:
est_0001_emails user when requesting password reset 0:00:01.624 ERROR
undefined local variable or method `login_path' for #<#<Class:0x007fc2db48d820>:0x007fc2df337e40>
But, visit login_path works fine if I remove #user = FactoryGirl.create(:user)
Is this a bug with Capybara? Or am I doing something wrong here?
I finally got this to work using the DatabaseCleaner gem using DatabaseCleaner.strategy = truncation. Here's what I ended up with:
test_helper.rb
ENV["RAILS_ENV"] = "test"
require File.expand_path("../../config/environment", __FILE__)
require "minitest/autorun"
require "capybara/rails"
require "active_support/testing/setup_and_teardown"
class IntegrationTest < MiniTest::Spec
include Rails.application.routes.url_helpers
include Capybara::DSL
register_spec_type(/integration$/, self)
def last_email
ActionMailer::Base.deliveries.last
end
def reset_email
ActionMailer::Base.deliveries = []
end
end
class HelperTest < MiniTest::Spec
include ActiveSupport::Testing::SetupAndTeardown
include ActionView::TestCase::Behavior
register_spec_type(/Helper$/, self)
end
Turn.config.format = :outline
# Database cleaner.
DatabaseCleaner.strategy = :truncation
factories.rb
FactoryGirl.define do
sequence :email do |n|
"email#{n}#example.com"
end
factory :user do
email
password "secret"
password_confirmation "secret"
end
end
integration/login_integration_test.rb
require "test_helper"
describe "Login integration" do
it "shouldn't allow an invalid login" do
visit login_path
click_button "Log In"
page.text.must_include "invalid"
end
it "should login a valid user" do
DatabaseCleaner.clean
user = FactoryGirl.create(:user)
visit login_path
within ".session" do
fill_in "session_email", :with => user.email
fill_in "session_password", :with => "secret"
end
click_button "Log In"
page.text.must_include "Logged in!"
save_and_open_page
end
end
Remove login_path and try to use url
for example
visit "/users/login" #login_path
I am using devise, rolify and cancan. I'm also using RSpec with FactoryBot for testing. Right now I'm working on some tests and I want to define users with different roles for those tests. Here is my current guess for how to do that using FactoryBot:
FactoryBot.define do
factory :user do
name 'Test User'
email 'example#example.com'
password 'please'
password_confirmation 'please'
# required if the Devise Confirmable module is used
confirmed_at Time.now
factory :admin do
self.has_role :admin
end
factory :curator do
self.has_role :curator
end
factory :super_admin do
self.has_role :super_admin
end
end
end
Here are some of my tests which don't seem to be written correctly:
require 'spec_helper'
describe "Pages" do
subject { page }
before do
#newpage = FactoryBot.create(:page)
#newpage.save
end
context 'not logged in' do
it_behaves_like 'not admin'
end
context 'logged in' do
context 'as user' do
before do
#user = FactoryBot.create(:user)
sign_in #user
end
it_behaves_like 'not admin'
end
context 'as curator' do
before do
#curator = FactoryBot.create(:curator)
sign_in #curator
end
it_behaves_like 'not admin'
end
context 'as admin' do
before do
#admin = FactoryBot.create(:admin)
sign_in #admin
end
it_behaves_like 'admin'
end
context 'as super admin' do
before do
#super_admin = FactoryBot.create(:super_admin)
sign_in #super_admin
end
it_behaves_like 'admin'
end
end
end
When I run the specs I these are my errors:
1) Pages logged in as admin behaves like admin can show page
Failure/Error: Unable to find matching line from backtrace
NoMethodError:
undefined method `has_role=' for #<User:0x007f883384d178>
Shared Example Group: "admin" called from ./spec/requests/pages_spec.rb:41
# ./spec/requests/pages_spec.rb:37:in `block (4 levels) in <top (required)>'
2) Pages logged in as curator behaves like not admin can show page
Failure/Error: Unable to find matching line from backtrace
ArgumentError:
Factory not registered: curator
Shared Example Group: "not admin" called from ./spec/requests/pages_spec.rb:32
# ./spec/requests/pages_spec.rb:28:in `block (4 levels) in <top (required)>'
3) Pages logged in as super admin behaves like admin can show page
Failure/Error: Unable to find matching line from backtrace
ArgumentError:
Factory not registered: super_admin
Shared Example Group: "admin" called from ./spec/requests/pages_spec.rb:50
# ./spec/requests/pages_spec.rb:46:in `block (4 levels) in <top (required)>'
Note: FactoryBot was previously called FactoryGirl
I would rather use FactoryBot's after(:create) callback to create roles (also see this issue for Rolify).
Furthermore the method has_role? is used to check if a user has a specific role, to set a specific role you should use the add_role method.
FactoryBot.define do
factory :user do
name 'Test User'
email 'example#example.com'
password 'please'
password_confirmation 'please'
# required if the Devise Confirmable module is used
confirmed_at Time.now
factory :admin do
after(:create) {|user| user.add_role(:admin)}
end
factory :curator do
after(:create) {|user| user.add_role(:curator)}
end
factory :super_admin do
after(:create) {|user| user.add_role(:super_admin)}
end
end
end
Note: FactoryBot was previously called FactoryGirl