I am at trying to set up testing in my app, and I have run into a problem with RSpec, FactoryGirl, and Mongoid. I have the following factory:
FactoryGirl.define do
factory :user do |u|
u.name { Faker::Name.name }
u.email { Faker::Internet.email }
u.crypted_password { Faker::Lorem.characters(10) }
u.password_salt { Faker::Lorem.characters(10) }
u.role :user
end
end
I try to use this factory in my tests:
require 'spec_helper'
describe User do
it "has a valid factory" do
create(:user).should be_valid
end
end
But I get this error:
1) User has a valid factory
Failure/Error: FactoryGirl.create(:user).should be_valid
NoMethodError:
undefined method `user' for #<User:0x007ff24a119b28>
# ./spec/models/user_spec.rb:5:in `block (2 levels) in <top (required)>'
I don't know what is causing this error. Also, is there a way to see a full stacktrace using rspec?
This line has problem
u.role :user
I guess you want to define a default role as "user"? Then don't use symbol or method, use string instead
u.role 'user'
Related
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
There are so many moving parts to Rails testing that it's difficult to debug a failing test sometimes.
As a case in point, I am attempting to use zeus, guard, rspec, capybara, Poltergeist, PhantomJS, and FactoryGirl to simply build a User factory.
I am met with the following error message:
spec/models/user.rb
require 'spec_helper'
describe User do
describe 'Factories' do
it { expect(build :user).to be_valid }
end
end
spec/factories/users.rb
FactoryGirl.define do
factory :user do
sequence(:email) { |n| Forgery(:internet).email_address }
sequence(:company) { |n| Forgery(:name).company_name }
password "password"
password_confirmation "password"
end
end
Now I have a request spec that fails on the following line, which is the setup at the beginning:
let!(:user) { login_user }
The failure is this:
Failure/Error: let!(:user) { login_user }
NoMethodError:
undefined method `last_logged_in_at=' for #<User:0x007fe60aca95e8>
# ./app/models/user.rb:136:in `set_login_time'
# ./app/controllers/sessions_controller.rb:24:in `create'
# ./spec/support/login_macros.rb:7:in `login_user'
# ./spec/requests/app_integration_spec.rb:5:in `block (2 levels) in <top (required)>'
# ./custom_plan.rb:12:in `spec'
# -e:1:in `<main>'
So the problem appears to be that since User is not a real object, there is no such attribute as last_logged_in_at. So I should have to mock that somewhere. I tried putting this inside the factory code:
before(:build) do |u|
u.stub!(:read_attribute).with(:last_logged_in_at).and_return(Time.now)
end
The error remains the same. The test simply can not find this attribute. Keep in mind this code works perfectly in the browser. I am out of my league. Where am I going wrong?
Is your test db schema up-to-date?
rake db:test:prepare
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.
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
I think I have a problem with configuring FactoryGirl with rails. I initially followed ASCIIcasts #275: how i test, but rake is giving me NameError: uninitialized constant ...
Am I missing something? Is it possible that some config file are wrong? I'm pretty new to RSpec and Rails.
I'm using Rails 3.2.2 + Mongoid + RSpec + factory_girl_rails.
Error:
Failures:
1) User should save user with valid required fields
Failure/Error: let(:user) { FactoryGirl.build(:valid_user) }
NameError:
uninitialized constant ValidUser
# ./spec/models/user_spec.rb:4:in `block (2 levels) in <top (required)>'
# ./spec/models/user_spec.rb:7:in `block (2 levels) in <top (required)>'
spec/factories.rb
FactoryGirl.define do
factory :valid_user do
name 'somename'
email 'a#b.com'
password 'somepassword'
end
end
spec/models/user_spec.rb
require 'spec_helper'
describe User do
let(:user) { FactoryGirl.build(:valid_user) }
it "should save user with valid required fields" do
user.should be_valid
end
end
spec/spec_helper.rb
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'rspec/autorun'
require 'capybara/rspec'
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
RSpec.configure do |config|
config.infer_base_class_for_anonymous_controllers = false
config.include FactoryGirl::Syntax::Methods
end
It's usually helpful to output the whole error, or at least the whole first sentence -- you haven't even told us what the missing constant is!
UPDATE: Thanks for the whole error. When you define the factory :valid_user, Factory Girl will automatically assume it is for a model named ValidUser. To get around this, you can either name your factory :user (assuming you have a User model), or you can try:
FactoryGirl.define do
factory :valid_user, :class => User do
name 'somename'
email 'a#b.com'
password 'somepassword'
end
end
Alternatively, if you want to have a couple different types of User factories, you can use:
FactoryGirl.define do
factory :user do
# set some attrs
end
factory :valid_user, :parent => :user do
name 'somename'
email 'a#b.com'
password 'somepassword'
end
factory :invalid_user, :parent => :user do
# some other attrs
end
end
You can declare factory like this........
Factory.define :organization do |g|
g.name 'Test Organization'
g.phone_number '5345234561'
g.website_url 'www.testorg.com'
g.city 'chichago '
g.association :state
end
And use it in organization_spec like this.....
require 'spec_helper'
describe Organization do
before :each do
#state = Factory :state
#organization = Factory :organization ,:state => #state
end
it "should be invalid without a name" do
#organization.name = nil
#organization.should_not be_valid
end
end
And enjoy!!!!!!!!!!!!!!!!