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
Related
I have a custom validator to verify the content of 2 fields in my database. When I use it through my UI, it works fine, however my rspec tests are failing and I can't understand why.
Here is the rspec test:
require 'rails_helper'
RSpec.describe Device, type: :model do
before(:each) { #user = User.create(email: 'test#test.com', password: 'password', password_confirmation: 'password') }
before(:each) { #device = Device.create(user_id: #user.id) }
subject { #device }
it { should allow_value('192.168.1.1').for(:ips_scan) }
it { should allow_value('192.168.1.1').for(:ips_exclude) }
it { should_not allow_value('192.168.1.1, a.b.c.d').for(:ips_scan) }
it { should_not allow_value('a.b.c.d').for(:ips_exclude) }
end
The Device model is:
class Device < ApplicationRecord
belongs_to :user
validates :ips_scan, :ips_exclude, ip: true, on: :update
end
And my ip_validator concern is:
class IpValidator < ActiveModel::Validator
def validate(record)
if record.ips_scan
ips = record.ips_scan.split(',')
ips.each do |ip|
/([0-9]{1,3}\.){3}[0-9]{1,3}(\/([1-2][0-9]|[0-9]|3[0-2]))?(-([0-9]{1,3}))?/ =~ ip
record.errors.add(:ips_scan, 'is not valid') unless $LAST_MATCH_INFO
end
end
if record.ips_exclude
ips = record.ips_exclude.split(',')
ips.each do |ip|
/([0-9]{1,3}\.){3}[0-9]{1,3}(\/([1-2][0-9]|[0-9]|3[0-2]))?(-([0-9]{1,3}))?/ =~ ip
record.errors.add(:ips_exclude, 'is not valid') unless $LAST_MATCH_INFO
end
end
end
end
Ironically, the validator is correctly passing the should_not allow_value tests, however the should allow_value tests are failing:
Failures:
1) Device should allow :ips_scan to be ‹"192.168.1.1"›
Failure/Error: it { should allow_value('192.168.1.1').for(:ips_scan) }
After setting :ips_scan to ‹"192.168.1.1"›, the matcher expected the
Device to be valid, but it was invalid instead, producing these
validation errors:
* ips_scan: ["is not valid"]
# ./spec/models/device_spec.rb:22:in `block (2 levels) in <top (required)>'
2) Device should allow :ips_exclude to be ‹"192.168.1.1"›
Failure/Error: it { should allow_value('192.168.1.1').for(:ips_exclude) }
After setting :ips_exclude to ‹"192.168.1.1"›, the matcher expected the
Device to be valid, but it was invalid instead, producing these
validation errors:
* ips_exclude: ["is not valid"]
# ./spec/models/device_spec.rb:23:in `block (2 levels) in <top (required)>'
At this point, I'm at a loss as to what is wrong now. Any help is much appreciated! Thx!
I found the solution. I don't know why, but the RSPEC doesn't know the $LAST_MATCH_INFO. If you replace it with $~, what it is actually is, it should work. At least, it works for me.
$LAST_MATCH_INFO is part of the library English which should be enabled in Rspec probably. But for me, the problem is resolved.
I'm basically getting an error when I'm trying to use a factory I created for a Post model in Ruby on Rails.
Here's the full error:
Failure/Error: post = create(:post)
NoMethodError:
undefined method `create=' for #<Post:0x007fbf1be6e510>
Did you mean? created_at=
# ./spec/models/post_spec.rb:6:in `block (2 levels) in <top (required)>'
Here's the file for the factories:
spec/factories/post.rb
FactoryGirl.define do
factory :post do
title "Hello"
content "Hello, my name is Jacob."
user create(:user)
user_id 1
end
end
spec/models/post_spec.rb
require 'rails_helper'
require 'spec_helper'
describe Post do
it "has a valid factory" do
post = create(:post)
expect(post).to(be_valid)
end
end
I do have a spec/support/factory_girl.rb file which includes the FactoryGirl::Syntax::Methods. This file is loaded by spec/rails_helper.rb.
Also, the create(:user) line works and I'm able to use the user factory in the rails console, but not the post factory.
Any help would be fantastic. Thank you!
In your post factory, you have the syntax wrong for defining an associated record. Try defining it like this:
FactoryGirl.define do
factory :post do
title "Hello"
content "Hello, my name is Jacob."
user
end
end
You just need to state that the post has a user, and you certainly shouldn't be setting them all to have a specific user_id ... since each post will create a new user unless told otherwise and you have no idea what user_id will be generated.
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
I have a new Rails 4 project with FactoryGirl and rSpec. In my spec_helper.rb I have:
# lots of stuff
RSpec.configure do |config|
# more stuff
config.include FactoryGirl::Syntax::Methods
end
I also removed the rspec/autorun require in this file.
A simple spec:
require 'spec_helper'
describe User do
build(:user)
end
with a simple factory:
FactoryGirl.define do
factory :user do
email "somename#someplace.com"
end
end
Fails with the following message.
`block in <top (required)>': undefined method `build' for #<Class:0x007fd46d0e3848> (NoMethodError)
However, if I explicitly qualify build in the spec like this it passes:
require 'spec_helper'
describe User do
FactoryGirl.build(:user)
end
What can I do so I don't have to add FactoryGirl every time?
The methods passed to config.include are only included by RSpec within the it, let, before and after blocks, not at the top level of describe. Since that's where you generally need to put your setup and test logic, in practice it's not really an issue.
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'