I have a users factory:
FactoryGirl.define do
factory :user, :aliases => [:assignee] do
sequence(:email) { |n| "foo#{n}#example.com" }
sequence(:username) { |n| "foo#{n}#example.com" }
profile
end
end
And a jobs factory:
FactoryGirl.define do
factory :job do
association :assignee, factory: :user
description "MyText"
completion_comment "MyText"
job_type "MyString"
...
end
end
Which are loaded into my jobs_feature_spec_helper:
def add_valid_job
click_job
within("#add_job") do
select 'foo1#example.com', from: 'Assignee'
fill_in_html("job__textarea", {:with => job[:description]})
click_button "Save"
end
end
My feature page spec passes in RSpec on my machine, but my employer sent me an email saying that the spec failed in Jenkins. Here's the message he sent:
Unable to find option "foo1#example.com <mailto:foo1#example.com>"
./spec/support/job_feature_spec_helpers.rb:11:in `block in add_valid_job'
./spec/support/job_feature_spec_helpers.rb:10:in `add_valid_job'
./spec/features/jobs/edit_job_line_spec.rb:8:in `block (2 levels) in <top (required)>'
So, it appears that when Jenkins runs the spec, it's not finding the assignee from the drop-down list, yet RSpec is. I have never personally used Jenkins, so if anyone has any advice that may help, I'd appreciate hearing it. Thanks!
The problem is about the sequence number. You can't use hardcorded 'foo1#example.com' in test.
There is no guarantee that the sequence number will start from 1. I have no time to figure out the reason, but just know the fact. In my tests I often see it from tens after running several tests.
I would suggest you to get the email from an existing user in db, created by FactoryGirl.
Although this was not your issue, one possible cause of getting different results between jenkins and test environments is using different DBs on each.
In my case I was using sqlite on jenkins and postgres on test, and that was generating different results.
Locally you can run
RAILS_ENV="jenkins" bundle exec rspec
to run specs on jenkins environment.
Related
So I am dealing with Rspec testing and FactoryGirl. I'm still a bit confused as to the inner workings of FactoryGirl when an object is created or built.
I have a few test results that look similar to this:
Failure/Error: #ticket = FactoryGirl.create(:ticket)
NoMethodError:
undefined method `user=' for #<Ticket:0x007f1455e72fc8>
# ./spec/models/ticket_spec.rb:38:in `block (3 levels) in <top (required)>'
It seems as if it is trying to assign an instance of ticket to user but why? And where would I start to try to fix it?
Update
I am just getting familiar with this code base and so I just realized there is no ticket factory or user factory. Is this way of creating a ticket for testing incorrect? Sorry for ignorance, I'm still very green with FactoryGirl and test frameworks in general.
You need to define your factories, most likely in spec/factories.rb or in a spec/factories folder. More info: https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md
# spec/factories.rb
factory :user do
# assuming this passes model validations
end
factory :ticket do
# assuming this passes model validations
user
end
TL;DR: How can I stop rspec aborting all specs when it encounters an error?
When running rails' built in testing suite, I get output similar to the following:
...F..F.EE...E
Finished in 0.64396 seconds
14 examples, 2 failures, 3 errors
The . represents a passing test, the F a failing test and the E an erroneous test. A failing test means the code of your rails application is failing, and an erroneous test means the code of your actual test is failing. Erroneous tests are never good.
What I do like about this though, is that when the program encounters an E, it keeps on going through all the other tests.
I'm using rpsec, and while I do like it, I sort of hate how, when it encounters an erroneous spec, it completely gives up the ghost, and quits all specs, like this:
12:39:37 - INFO - Running: spec
/usr/local/rvm/rubies/ruby-1.9.3-p392/lib/ruby/gems/1.9.1/gems/activerecord-4.0.1/lib/active_record/validations.rb:57:in `save!': Validation failed: Email has already been taken (ActiveRecord::RecordInvalid)
from /usr/local/rvm/rubies/ruby-1.9.3-p392/lib/ruby/gems/1.9.1/gems/activerecord-4.0.1/lib/active_record/attribute_methods/dirty.rb:41:in `save!'
from /usr/local/rvm/rubies/ruby-1.9.3-p392/lib/ruby/gems/1.9.1/gems/activerecord-4.0.1/lib/active_record/transactions.rb:275:in `block in save!'
from /usr/local/rvm/rubies/ruby-1.9.3-p392/lib/ruby/gems/1.9.1/gems/activerecord-4.0.1/lib/active_record/transactions.rb:326:in `block in with_transaction_returning_status'
While I do like the error reporting, I really want it to carry on running all specs when it encounters an erroneous spec. Rather than the above output, I really want something along the lines of the following:
12:40:01 - INFO - Running: spec
...F..F..E
Failures:
1) User A new user can be created email should == "awesomedog#hotmail.co.uk"
Failure/Error: its(:email) { should == "awesomedog#hotmail.co.uk" }
expected: "awesomedog#hotmail.co.uk"
got: "awesomedozg#hotmail.co.uk" (using ==)
# ./spec/model/user_spec.rb:11:in `block (3 levels) in <top (required)>'
2) User A new user can be created should not be valid
Failure/Error: it { should_not be_valid }
expected valid? to return false, got true
# ./spec/model/user_spec.rb:20:in `block (3 levels) in <top (required)>'
Errors:
1) `save!': Validation failed: Email has already been taken (ActiveRecord::RecordInvalid)
Finished in 0.64396 seconds
10 examples, 2 failures, 1 error
Failed examples:
rspec ./spec/model/user_spec.rb:11 # User A new user can be created email should == "awesomedog#hotmail.co.uk"
rspec ./spec/model/user_spec.rb:20 # User A new user can be created should not be valid
I'm using factory-girl and factories in my specs, and data-base cleaner to clean my database between each spec. I'm using rspec-guard to run all specs on the event of any project file (apart from those in tmp,log or db) being saved. Because rpsec keeps wimping out if it hits an error, I'm getting this error:
How can I clean my database between erroneous rspec specs?
Basically, database-cleaner is configured to clean my database when a spec begins and when a spec finishes. Because rspec quits in the middle of a spec when it hits an error, database-cleaner doesn't detect that the spec has finished, and so never cleans my database. This means I have to manually empty it with my database shell.
I'd also prefer to see the state of all my other specs, even if one is erroneous! Rspec is really silly in this regard methinks!
Here are my factories and my spec:
spec/model/user_spec.rb:
require 'spec_helper'
describe User do
context "Valid user" do
user = FactoryGirl.create(:user_with_all_valid)
subject { user }
its(:first_name) { should == "Jimmy" }
its(:last_name) { should == "Thehat" }
its(:profile_name) { should == "Jimbohatboy893" }
its(:email) { should == "awesomedog#hotmail.co.uk" }
its(:password) { should == "thisisasupersecretpassword12234234" }
its(:password_confirmation) { should == "thisisasupersecretpassword12234234" }
end
end
spec/factories.rb:
FactoryGirl.define do
factory :user_with_all_valid, class: User do
first_name "Jimmy"
last_name "Thehat"
profile_name "Jimbohatboy893"
email "awesomedog#hotmail.co.uk"
password "thisisasupersecretpassword12234234"
password_confirmation "thisisasupersecretpassword12234234"
end
end
Your issue is this line:
context "Valid user" do
user = FactoryGirl.create(:user_with_all_valid) # Right here!
...
Test setup should always be done in a before block; variable definition is done in a let block. Doing it in your test definition will cause rspec to fail as described. In general, you should only ever have rspec directives (before, after, let, subject, it, its, etc) in a context block. If you're running test setup, application code, whatever, you're going to open yourself up to this kind of problem. What you want is:
context "Valid user" do
let(:user) { FactoryGirl.create(:user_with_all_valid) }
or:
context "Valid user" do
before { #user = FactoryGirl.create(:user_with_all_valid) }
The suite will then run the whole thing as expected, and report failures for the tests that fail to do their defined setup (the user definition) properly, rather than bombing out of the whole suite.
You need to have a unique email each time due to the validation on the model.
btw the other 7 or 8 are passing (dots).
Try adding:
new_user_time = Time.new
to save the current time.
Then pass that in to the create user factory.
Then use it to check that the users email is correct.
Use sequences as detailed at: https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#sequences
Unique values in a specific format (for example, e-mail addresses) can be generated using sequences. Sequences are defined by calling sequence in a definition block, and values in a sequence are generated by calling FactoryGirl.generate:
# Defines a new sequence
FactoryGirl.define do
sequence :email do |n|
"person#{n}#example.com"
end
end
FactoryGirl.generate :email
# => "person1#example.com"
FactoryGirl.generate :email
# => "person2#example.com"
...
The just have your spec check that part of the email address is correct, e.g. the #something.com or use a regular expression to compare, e.g. match(/person.*#something\.com/)
For example:
I run a test and all asserts pass, but I run the test again, and in my case, I receive the following error:
Validation failed: Email has already been taken
It seems adding: sequence(:email) {|n| "nobody#{n}#xyz.com" } for factorygirl is pointless
The tests seem to pass sometimes and others fail for errors reasons like these.
Please advise on the problem/solution.
try deleting all the records from tables before running test case.
eg:-
describe User do
before(:each) do
User.delete_all
end
it "validate e-mail" do
(do staff..)
end
end
I´m not sure it is a definitive solution, but i added random numbers to my product references on the factories with factorygirl using the lazy attributes.
Example:
FactoryGirl.define do
factory :product do
reference {"AB"+rand(999).to_s}
description "Test Product"
quantity {(1..9999).to_a.sample}
price_per_unit {((1..999999).to_a.sample)/100.to_f}
end
end
Do you have any "before(:all)" block? maybe you are missing the corresponding "after(:all)" to clean the data. before(:each) acts as a transaction so the database gets cleaned, before(:all) works like a normal query, yout have to handle the cleanup in an after(:all) block.
Like suggested from the title, I've a strange mistake. In fact when i try to make an operation reserved to the test enviroment (for example: rake test:prepare), in some way it influence the other environments and delete all the record in my database (mysql).
(I'm using rspec, and i'm following the tutorials from Michael Hartl.)
Another example (maybe clearer) is when I write the tests that implicate the creation and the cancellation of new users throught FactoryGirl:
Location: spec/factories.rb
FactoryGirl.define do
factory :user do
sequence(:name) {|n| "Person #{n}"}
sequence(:email) {|n| "person_#{n}#example.it"}
password "luckyluke"
password_confirmation "luckyluke"
factory :admin do
admin true
end
end
Location: spec/requests/users_pages_spec.rb
describe "pagination" do
before(:all) { 30.times { FactoryGirl.create(:user) } }
after(:all) { User.delete_all }
# tests...
end
Although I'm into the test enviroment, when i run
bundle exec rspec spec/requests/users_pages_spec.rb , rails really create 30 users on my database, and ater really delete ALL the users from database. (Also the users created and stored before the test!!).
P.S. sorry for my english
Mocking the database should happen before each test, to ensure isolation. This way, FactoryGirl is also responsible for deleting all its created (as is described in this answer):
describe "pagination" do
before(:each) { 30.times { FactoryGirl.create(:user) } }
# tests...
end
Also, it is advisable that the database will not contain any data from before the test, since it might change the behavior of the tests - it should contain only what the test expects it to contain.
I've very green to this TDD business, so any help would be fantastic!
So, I've got a factory with the following:
FactoryGirl.define do
factory :account do
email "example#example.com"
url "teststore"
end
end
And an Rspec test with:
it "fails validation without unique email" do
account1 = FactoryGirl.create(:account)
account2 = FactoryGirl.create(:account)
account2.should have(1).error_on(:email)
end
I get a failure with the following message:
1) Account fails validation without unique email
Failure/Error: account2 = FactoryGirl.create(:account)
ActiveRecord::RecordInvalid:
Validation failed: Email taken, please choose another, Url taken, please choose another
# ./spec/models/account_spec.rb:11:in `block (2 levels) in <top (required)>'
Is this the correct way to create new factories? Any ideas what I'm doing wrong here (I have no doubt I'm doing something totally incorrect!)
EDIT: I'm thinking of instead of using 'create' on the second account, I may want to use .build and then .save instead?
Save yourself the database interactions and use the build method for situations like this.
it "fails validation without unique email" do
account1 = create(:account)
account2 = build(:account)
account2.should_not be_valid
account2.should have(1).error_on(:email)
end
You don't need to try and create an account for valid? to return false. You have access to the errors object on the account even when it's just built in memory. This will decrease database interactions and thus making your tests much faster.
Have you considered using sequences in your factories? I don't know how far along you are with your RSpec / FactoryGirl experience, but you will find that things like the following are very useful.
factories.rb
factory :account do
sequence(:email) { |n| "user#{n}#example.com" }
url "teststore"
end
Every time you call build or create on the account factory, you will get unique emails.
Remember that you can always specify values for the attributes on the factory using the options hash. So when testing your uniqueness validation on the account, you would do something like this.
it "fails validation without unique email" do
account1 = create(:account, :email => "foo#bar.com")
account2 = build(:account, :email => "foo#bar.com")
account2.should_not be_valid
account2.should have(1).error_on(:email)
end
Try this:
FactoryGirl.create(:account)
lambda {
FactoryGirl.create(:account)
}.should raise_error(ActiveRecord::RecordInvalid)
This will help - with similar syntax to what you're doing.
However, searching for "rspec validate_uniqueness_of" will find you some more elegant approaches instead of using factory girl like this!