avoid factorygirl repeate the number when run second time in cucumber - ruby-on-rails

Its my factorygirl code
FactoryGirl.define do
factory :account do
sequence :name do |n|
"Test Account#{n}"
end
end
end
This is my method for run factorygirl code
def create_accounts n=2
n.times do
FactoryGirl.create(:account, subscription_ids: #sub.id.to_s)
end
end
My problem is, first time my FactoryGirl output is Test Account1, Test Account2, When i execute second time, It create output as Test Account3, Test Account4. But I need Test Account1, Test Account2 when run multiple time. How may i do this.
Thanks for your advices

FactoryGirl is designed to create new unique records every time you call #create. If you want to keep the original record set around, you should save them to a variable and then return them rather than running FactoryGirl.create again.

You can also use database_cleaner gem to clean the database after every test. This helps to prevent any problems rising from the state of the database.

I solve this problem after replace this code
def create_accounts n=1
create_subscription
n.times do |r|
r += 1
FactoryGirl.create(:account, subscription_ids: #sub.id.to_s, name: "Test Account#{r}")
end
end
Updated
I am using cucumber-> capybara -> selenium
Reset the factory girl sequence
add this code in spec->support->reset.rb
module FactoryGirl
def self.reload
self.factories.clear
self.sequences.clear
self.traits.clear
self.find_definitions
end
end
Add this in env.rb
After do
FactoryGirl.reload
end

Related

Rspec/FactoryGirl uniqueness validations in a large test suite

Using Rspec and FactoryGirl, if I have a factory that autoincrements a trait using a sequence, and in some specs if I explicitly set this trait, with a large enough test suite, sometimes random specs fail with
Validation failed: uniq_id has already been taken
The factory is defined like this:
factory :user { sequence(:uniq_id) {|n| n + 1000} }
I'm guessing this validation fails because in one place in my test suite, I generate a user like this:
create(:user, uniq_id: 5555)
And because presumably factory girl is generating more than 4,555 users over the suite, the validation is failing?
I'm attempting to avoid this problem by just turning the uniq_id into 55555 (larger number), so there is no interference. But is there a better solution? My spec_helper includes these relevant bits:
config.use_transactional_fixtures = true
config.after(:all) do
DatabaseCleaner.clean_with(:truncation)
end
It happens to me sometimes. I didn't found any explanation, but happens only with big set of data. I let someone find the explanation!
When it happens, you can declare your attribute like this (here is an example using faker gem) :
FactoryGirl.define do
factory :user do
login do
# first attempt
l = Faker::Internet.user_name
while User.exists?(:login => l) do
# Here is a loop forcing validation
l = Faker::Internet.user_name
end
l # return login
end
end
end
I was able to solve my issue like this in my factory (based on #gotva's suggestion in the question comments).
factory :user do
sequence(:uniq_id) { |n| n + 1000 }
# increment again if somehow invalid
after(:build) do |obj|
if !obj.valid? && obj.errors.keys.include?(:uniq_id)
obj.uniq_id +=1
end
end
end

About transaction using in RSpec test

I met a very strange issue when writing test using RSpec. Assume that I have 2 models: Company and Item with association company has_many items. I also set up database_cleaner with strategy transaction. My RSpec version is 2.13.0, database_cleaner version is 1.0.1, rails version is 3.2.15, factory_girl version is 4.2.0. Here is my test:
let(:company) { RSpec.configuration.company }
context "has accounts" do
it "returns true" do
company.items << FactoryGirl.create(:item)
company.items.count.should > 0
end
end
context "does not have accounts" do
it "returns false" do
company.items.count.should == 0
end
end
end
I set up an initial company to rspec configuration for using in every test because I don't want to recreate it in every tests because creating a company takes a lot of time(due to its callbacks and validations). The second test fails because item is not cleaned from the database after the first test. I don't understand why. If I change line company.items << FactoryGirl.create(:item) to FactoryGirl.create(:item, company: company), the it passes. So can any body explain for me why transaction isn't rollbacked in the first situation. I'm really messed up
Thanks. I really appreciate.
I think the problem is not in the rollback and I'm wondering if company.items can store it's value between contexts but I'm not sure.
I'm unable to reproduce it quickly so I want to ask you to:
check log/test.log when the rollback is performed
how many INSERTs was made for company.items << FactoryGirl.create(:item)
than change on the first test > to < that way: company.items.count.should < 0 it'll make test to fail but you'll get count value. Is it 1 or 2 ?
If you have relation between Company and Item model like has_many/belongs_to than I would suggest to just use build(:item) which should create company for it as well:
for example:
let(:item) { FactoryGirl.build(:item) }
context "has accounts"
it "returns true" do
item.save
Company.items.count.should == 1
end
don't forget to include association :company line at :item's factory
Hint:
add to spec_helper.rb:
RSpec.configure do |config|
# most omitted
config.include FactoryGirl::Syntax::Methods
and you can call any FactoryGirl method directly like this:
let(:item) { build(:item) }

Rspec Running Tests Results in Validation Errors

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.

File upload, factory_girl & database_cleaner

In my model, I have to choose an asset, saved in a editorial_asset table.
include ActionDispatch::TestProcess
FactoryGirl.define do
factory :editorial_asset do
editorial_asset { fixture_file_upload("#{Rails.root}/spec/fixtures/files/fakeUp.png", "image/png") }
end
end
so I have attached in my model factory an association on :editorial_asset
Upload work great, but take too much time (1s per example)
I'm wonder if it's possible to create uploads one time before each examples, and say in the factory: "find instead of create"
But the problem with database_cleaner, I cannot except tables with :transaction, truncation take 25sec instead of 40ms !
EDIT
The factory that need an asset
FactoryGirl.define do
factory :actu do
sequence(:title) {|n| "Actu #{n}"}
sequence(:subtitle) {|n| "Sous-sitre #{n}"}
body Lipsum.paragraphs[3]
# Associations
user
# editorial_asset
end
end
The model spec
require 'spec_helper'
describe Actu do
before(:all) do
#asset = create(:editorial_asset)
end
after(:all) do
EditorialAsset.destroy_all
end
it "has a valid factory" do
create(:actu).should be_valid
end
end
So a working way is
it "has a valid factory" do
create(:actu, editorial_asset: #asset).should be_valid
end
but there's no way to inject automatically association ?
Since you're using RSpec, you could use a before(:all) block to set up these records once. However, anything done in a before-all block is NOT considered part of the transaction, so you will have to delete anything from the DB yourself in an after-all block.
Your factory for the model that has an association to the editorial asset could then, yes, try to first find one before creating it. Instead of doing something like association :editorial_asset you could do:
editorial_asset { EditorialAsset.first || Factory.create(:editorial_asset) }
Your rspec tests could then look like this:
before(:all) do
#editorial = Factory.create :editorial_asset
end
after(:all) do
EditorialAsset.destroy_all
end
it "already has an editorial asset." do
model = Factory.create :model_with_editorial_asset
model.editorial_asset.should == #editorial
end
Read more about before and after blocks on the Rspec GitHub wiki page or on the Relish documentation:
https://github.com/rspec/rspec-rails
https://www.relishapp.com/rspec

How can I reset a factory_girl sequence?

Provided that I have a project factory
Factory.define :project do |p|
p.sequence(:title) { |n| "project #{n} title" }
p.sequence(:subtitle) { |n| "project #{n} subtitle" }
p.sequence(:image) { |n| "../images/content/projects/#{n}.jpg" }
p.sequence(:date) { |n| n.weeks.ago.to_date }
end
And that I'm creating instances of project
Factory.build :project
Factory.build :project
By this time, the next time I execute Factory.build(:project) I'll receive an instance of Project with a title set to "project 3 title" and so on. Not surprising.
Now say that I wish to reset my counter within this scope. Something like:
Factory.build :project #=> Project 3
Factory.reset :project #=> project factory counter gets reseted
Factory.build :project #=> A new instance of project 1
What would be the best way to achieve this?
I'm currently using the following versions:
factory_girl (1.3.1)
factory_girl_rails (1.0)
Just call FactoryGirl.reload in your before/after callback. This is defined in the FactoryGirl codebase as:
module FactoryGirl
def self.reload
self.factories.clear
self.sequences.clear
self.traits.clear
self.find_definitions
end
end
Calling FactoryGirl.sequences.clear is not sufficient for some reason. Doing a full reload might have some overhead, but when I tried with/without the callback, my tests took around 30 seconds to run either way. Therefore the overhead is not enough to impact my workflow.
After tracing my way through the source code, I have finally come up with a solution for this. If you're using factory_girl 1.3.2 (which was the latest release at the time I am writing this), you can add the following code to the top of your factories.rb file:
class Factory
def self.reset_sequences
Factory.factories.each do |name, factory|
factory.sequences.each do |name, sequence|
sequence.reset
end
end
end
def sequences
#sequences
end
def sequence(name, &block)
s = Sequence.new(&block)
#sequences ||= {}
#sequences[name] = s
add_attribute(name) { s.next }
end
def reset_sequence(name)
#sequences[name].reset
end
class Sequence
def reset
#value = 0
end
end
end
Then, in Cucumber's env.rb, simply add:
After do
Factory.reset_sequences
end
I'd assume if you run into the same problem in your rspec tests, you could use rspecs after :each method.
At the moment, this approach only takes into consideration sequences defined within a factory, such as:
Factory.define :specialty do |f|
f.sequence(:title) { |n| "Test Specialty #{n}"}
f.sequence(:permalink) { |n| "permalink#{n}" }
end
I have not yet written the code to handle: Factory.sequence...
There is a class method called sequence_by_name to fetch a sequence by name, and then you can call rewind and it'll reset to 1.
FactoryBot.sequence_by_name(:order).rewind
Or if you want to reset all.
FactoryBot.rewind_sequences
Here is the link to the file on github
For googling people: without further extending, just do FactoryGirl.reload
FactoryGirl.create :user
#=> User id: 1, name: "user_1"
FactoryGirl.create :user
#=> User id: 2, name: "user_2"
DatabaseCleaner.clean_with :truncation #wiping out database with truncation
FactoryGirl.reload
FactoryGirl.create :user
#=> User id: 1, name: "user_1"
works for me on
* factory_girl (4.3.0)
* factory_girl_rails (4.3.0)
https://stackoverflow.com/a/16048658
According to ThoughBot Here, the need to reset the sequence between tests is an anti-pattern.
To summerize:
If you have something like this:
FactoryGirl.define do
factory :category do
sequence(:name) {|n| "Category #{n}" }
end
end
Your tests should look like this:
Scenario: Create a post under a category
Given a category exists with a name of "My Category"
And I am signed in as an admin
When I go to create a new post
And I select "My Category" from "Categories"
And I press "Create"
And I go to view all posts
Then I should see a post with the category "My Category"
Not This:
Scenario: Create a post under a category
Given a category exists
And I am signed in as an admin
When I go to create a new post
And I select "Category 1" from "Categories"
And I press "Create"
And I go to view all posts
Then I should see a post with the category "Category 1"
Had to ensure sequences are going from 1 to 8 and restart to 1 and so on. Implemented like this:
class FGCustomSequence
def initialize(max)
#marker, #max = 1, max
end
def next
#marker = (#marker >= #max ? 1 : (#marker + 1))
end
def peek
#marker.to_s
end
end
FactoryGirl.define do
factory :image do
sequence(:picture, FGCustomSequence.new(8)) { |n| "image#{n.to_s}.png" }
end
end
The doc says "The value just needs to support the #next method." But to keep you CustomSequence object going through it needs to support #peek method too. Lastly I don't know how long this will work because it kind of hack into FactoryGirl internals, when they make a change this may fail to work properly
There's no built in way to reset a sequence, see the source code here:
http://github.com/thoughtbot/factory_girl/blob/master/lib/factory_girl/sequence.rb
However, some people have hacked/monkey-patched this feature in. Here's an example:
http://www.pmamediagroup.com/2009/05/smarter-sequencing-in-factory-girl/
To reset particular sequence you can try
# spec/factories/schedule_positions.rb
FactoryGirl.define do
sequence :position do |n|
n
end
factory :schedule_position do
position
position_date Date.today
...
end
end
# spec/models/schedule_position.rb
require 'spec_helper'
describe SchedulePosition do
describe "Reposition" do
before(:each) do
nullify_position
FactoryGirl.create_list(:schedule_position, 10)
end
end
protected
def nullify_position
position = FactoryGirl.sequences.find(:position)
position.instance_variable_set :#value, FactoryGirl::Sequence::EnumeratorAdapter.new(1)
end
end
If you are using Cucumber you can add this to a step definition:
Given(/^I reload FactoryGirl/) do
FactoryGirl.reload
end
Then just call it when needed.

Resources