I have this Factory:
Factory.define :email_address do |e|
e.sequence(:address) { |n| "factory_#{n}#example.com" }
e.validated true
end
When I run my specs with rake spec, it works fine.
When I run autospec, it fails right away, claiming that the email address is being used twice in two different objects (there is a validation which restricts this).
Why is it behaving differently under autospec?
Sometimes, when you abort a test suite with Ctrl+C, it may leave your database dirty. As your database is dirty, creating new objects is going to have validation conflicts. Just run rake db:test:clone again and you should be fine.
I suspect what is happening is that FactoryGirl is resetting that n on each invocation from autospec, but the database hasn't been emptied.
First, to check this diagnosis, change your factory to the following:
Factory.define :email_address do |e|
e.sequence(:address) { |n| puts "Email ##{n}"; "factory_#{n}#example.com" }
e.validated true
end
If my diagnosis is correct, there are two possible fixes:
change FactoryGirl to start from an index greater than the maximum ID used. This would involve some serious hacking on Factory::Sequence - you would probably have to turn Factory::Sequence.sequences from a Hash[Symbol => Proc] to a Hash[Symbol => [Proc, Integer] that remembered the highest index it used. Indeed, that might not even work since autospec does seem to be unloading the FactoryGirl classes properly (otherwise the sequence lookup wouldn't be failing and creating a new Factory::Sequence object).
figure out why your database isn't being cleared between each autospec run. Have you checked your teardown methods? Does your testing database support transactions?
Related
I have a simple test in my Rails app which is consistently taking much longer than other apparently similar tests.
It takes 1 to 2 seconds of real time to run test_invalid_without_name as below:
VALID_PARAMS = { name: 'Client record' }
def test_invalid_without_name
c = Client.new(VALID_PARAMS)
c.name = nil
refute c.valid?, 'should not be valid without a name'
end
The next test, test_valid_with_all_params takes less than 1/100th of a second:
def test_valid_with_all_params
c = Client.new(VALID_PARAMS)
assert c.valid?, 'should be valid with appropriate parameters'
end
The Client model is totally straightforward at this stage:
class Client < ActiveRecord::Base
belongs_to :entity, polymorphic: true
validates :name, presence: true
end
Can anyone either spot what’s wrong here, or give me an idea where I should look next to try to figure this out?
Update 1
I have used ruby-prof to profile the code, and it seems much of the time is being spent in the Psych gem. I believe this is used in ActiveRecord::..#serialize, which I am using in another model as follows:
class Opportunity < ActiveRecord::Base
belongs_to :client
serialize :details, Hash
end
However, removing the call to serialize here doesn’t make any difference to the Client tests (and I can’t see why it would, the two classes are associated, but no Opportunities are instantiated in the Client test).
Update 2
Turns out Psych is being called because I18n is using it. My assumption at this point is that the failed validation causes I18n to go to the locale files to pull out an error message. I will look into stubbing out I18n for testing...
It turns out a suitable approach to solving this was to use ruby-prof as follows.
Add to Gemfile (in the development group):
gem 'ruby-prof'
Wrap the test code in calls to the profiler:
def test_invalid_without_name
RubyProf.start
cr = Client.new(VALID_PARAMS)
cr.name = nil
refute cr.valid?, 'should not be valid without a name'
result = RubyProf.stop
printer = RubyProf::CallStackPrinter.new(result)
printer.print(File.open(File.join(Rails.root, 'profile_invalid_without_name.html'), 'w'))
end
This will create an interactive HTML document at the root of your Rails app, which shows the breakdown of where the time is spent.
In this case, it was 80%+ in I18n looking for translations. I added the following line to config/environments/test.rb to stub it out in testing:
I18n.backend = I18n::Backend::KeyValue.new({})
I will probably make the following additional improvements in future:
Be more specific with where I stub out I18n, so that full-stack acceptance tests can use the real thing.
Write a more convenient profiling approach. It looks like ruby-prof offers various other mechanisms for activating it, this was just the simplest for initial use.
It's not possible to answer the question without more information.
Does this happen when you run the each of tests individually?
Do you have anything in the setup/teardown or your test_helper.rb?
Does it happen if you pass a hash with name => nil instead of setting it?
Did you redefine the name setter?
Do you have any callbacks on your model?
I recently upgraded an app i was working on to rails 3.1rc5..
For the most part it's been great but a few of my tests are having really weird issues..
For example in one of my cucumber specs i create a bunch of fake records using factory girl.. usually this works fine but it seemed that it wasn't creating the records for some reason..
So I commented out all of my factory stuff and replaced it with this:
c = Contact.new(:first_name => "SOMEONE", :last_name => "COOL", :dob => 10.years.ago, :sex => "male")
if c.save
puts "MYCOUNT: #{Contact.count}"
else
puts "EXPLOSIONS!!!"
end
Running this as part of the cucumber suite outputs this:
MYCOUNT: 0
So the contact record is obviously being saved (and passing validations) yet it is still not showing up when i call count??
Why??
I am using:
Rails 3.1rc5
rspec-rails
cucumber-rails
and
factory_girl_rails
I should also probably note i'm indexing my models using sunspot (solr API) https://github.com/outoftime/sunspot
It sounds like you've got a transaction rollback firing within your test:
1) Transaction opened
2) Contact.save succeeds (now have legit Contact instance and db record)
3) Something goes wrong, raises ActiveRecord::Rollback
4) Transactions rolls back, leaving legit Contact instance but no db record, count = 0
I don't know what would cause this related to your Rails upgrade, but perhaps this will help you find what's failing.
EDIT:
If you tail your log/test.log file you should see a Rollback occur if this is the case. You can look at the previous DB activity to get a clue as to what the last successful db operation was before it.
Not an exact science, but may help you decide if this is the case and get a rough idea of where it went sideways.
I'd like to init the data base once everytime i run tests, rather than every test.
I know with Rspec there is before(:all), but I haven't been able to get that working. I was wondering if rails had something similar.
Firstly: there used to be a before(:all) equivalent in Test::Unit but it was removed (don't know why).
Secondly: there are very good reasons not to do what you are trying to do - tests are meant to be run independently of one another, not rely on state that's in the db. This way you can guarantee that it's testing exactly what you are expecting it to test.
If you have one test that changes the state of the db, and you move it and it runs after another test which expects it to be another state - you run into problems. Thus, all test must be independent.
Thus: the db is rolled back to its pristine state and re-seeded every time.
If you really want some state that the db is always in - then set it up in the fixtures... and just realise that the db will be re-loaded for each test.
If you are having trouble with load-times... then consider figuring out some other way around the problem - eg don't use huge numbers of fixtures, instead use Factories to only create the data that you need for each individual test.
If there's some other reason... let us know - we may have a solution for it.
Edit: if you really need it, I actually wrote a monkey patch for this a long while back:
"faking startup and shutdown"
All things to run before everything just go in the top of the class
require 'test_helper'
class ObjectTest < ActiveSupport::TestCase
call_rake("db:bootstrap RAILS_ENV=test")
#set up our user for doing all our tests (this person is very busy)
#user = Factory(:user)
#account = Factory(:account)
#user.account = #account
#user.save
# make sure our user and account got created
puts "||||||||||||||||||||||||||||||||||||||||||||||"
puts "| propsal_test.rb"
puts "| #{#user.name}"
puts "| #{#user.account.name}"
puts "||||||||||||||||||||||||||||||||||||||||||||||"
Similar to the problem described here:
http://rpheath.com/posts/411-how-to-use-factory-girl-with-rspec
in Short (shorten'd code):
spec_helper:
config.use_transactional_fixtures = true
config.use_instantiated_fixtures = false
factories.rb:
Factory.define :state do
f.name "NY"
end
in my spec
before(:each) do
#static_model = Factory(:state) # with validate uniqueness of state name
end
error:
duplicate entry name "NY" etc.
Question:
Shouldn't rspec clear database before each spec example and hence not throwing duplicate entry errors?
Things i think off:
do you use rake spec to run your testsuite: that builds up the database from scratch (to make sure nothing is sticking)
do you use, anywhere, a before (:all) ? Because whatever you create inside a before :all should be deleted again in a after :all or it keeps on existing.
Question: Shouldn't rspec clear database before each spec example and hence not throwing duplicate entry errors?
RSpec with DatabaseCleaner or RSpec Rails with use_transactional_fixtures will clear the DB as long as your created the data in the example itself. before :all do ... end is considered outside of the example, because the data remains untouched across multiple examples. Whatever you create in before :all you have to delete in after :all.
In order to delete whatever you create automatically use before :each do ... end. Be aware the same data will be created and removed 10 times if you have 10 examples. The difference between before :all and before :each is better explained here: rails rspec before all vs before each
Some more possible causes:
There's still a states.yml fixture sitting around
Someone played around on script/console test and forgot to clean up afterwards.
You might also find it's because you haven't wrapped the statement in:
describe "what it should do" do
#static_model = Factory(:state) # with validate uniqueness of state name
end
I discovered that was the change that solved this problem:
Why isn't factory_girl operating transactionally for me? - rows remain in database after tests
I have had similar questions about what sort of starting state one can expect when using FG and RSpec.
While I too wait for clarity, Database Cleaner could be a good fix: http://rubydoc.info/gems/database_cleaner/0.6.7/frames
hth -
Perry
When you use Factory(:state) wich is a shortcut to Factory.create(:state), factory_girl returns you a saved object.
Use Factory.build(:state) instead.
Dude maybe your yaml fixtures from regular unit tests get mixed into your rspec?
I'm trying to perform an integration test via Watir and RSpec. So, I created a test file within /integration and wrote a test, which adds a test user into a base via factory_girl.
The problem is — I can't actually perform a login with my test user. The test I wrote looks as following:
...
before(:each)
#user = Factory(:user)
#browser = FireWatir::Firefox.new
end
it "should login"
#browser.text_field(:id, "username").set(#user.username)
#browser.text_field(:id, "password").set(#user.password)
#browser.button(:id, "get_in").click
end
...
As I'm starting the test and see a "performance" in browser, it always fires up a Username is not valid error.
I've started an investigation, and did a small trick. First of all I've started to have doubts if the factory actually creates the user in DB. So after the immediate call to factory I've put some puts User.find stuff only to discover that the user is actually in DB. Ok, but as user still couldn't have logged in I've decided to see if he's present in DB with my own eyes.
I've added a sleep right after a factory call, and went to see what's in the DB at the moment. I was crushed to see that the user is actually missing there! How come? Still, when I'm trying to output a user within the code, he is actually being fetched from somewhere. So where does the records, made by factory_girl within a runtime lie? Is it test or dev DB? I don't get it.
I've 10 times checked if I'm running my Mongrel in test mode (does it matter? I think it does, as I'm trying to tun an integration test) and if my database.yml holds the correct connection specific data.
I'm using an authlogic, if that can give any clue (no, putting activate_authlogic doesn't work here).
Don't forget that RSpec is probably using transations when running the specs. RSpec will wrap the execution of the spec within a transaction and rollback at the end. It means you won't be able to see the record from outside that transaction (i.e. from another SQL connection).
If you want to ensure the user record is actually created by Factory Girl, you can do something like:
before(:each)
#user = Factory(:user)
User.find_by_username(#user.username).should_not be_nil
#browser = FireWatir::Firefox.new
end
Somehow the solution went strange — I put factories to before(:all) block, and all the stuff worked as it should.
Factory Girl is going to create temporary DB entries in your test database. Your tests database is going to be cleared out after each test.