How can I use rspec with YAML fixtures that get cleaned up? - ruby-on-rails

I want my rspec tests to load my YAML fixtures, and I want them to be loaded inside the transaction, so that they disappear after each test.
describe "the thing I'm testing"
fixtures :sandwiches, :condiments
before(:each) do
# stuff
end
it "makes sandwiches"
# making sandwiches
end
end
When the call to fixtures is there (in the describe block, but not in the before block), it loads my sandwiches and condiments, but outside of the transaction, and thus committed. Well, it looks that way; what's certain is that my fixtures are still in the db after the test finishes.
I thought perhaps I could put the call to fixtures in the before block, inside the transaction, but then I get
NoMethodError: undefined method `fixtures' for
<RSpec::Core::ExampleGroup::Nested_1:0x007fd1f11f7578>
So I guess I could use DatabaseCleaner, or something similar, but...can't I get what I want without that?

fixtures is a Rails method (not RSpec) that, afaik, doesn't actually load the fixtures - just registers them for loading later.
It is a class method, so it has to run in the class (the ExampleGroup returned by describe) and not the instance in which before, it, etc are evaluated.
Do you have config.use_transactional_fixtures = true in spec_helper.rb?

Related

Intermittent ActiveRecord::RecordNotFound during Ruby on Rails Integration Tests

I have integration tests that are intermittently failing, and always with ActiveRecord::RecordNotFound errors. The error takes place inside the controller where a find call takes place given an ID from a fixture. It takes place in multiple controllers, though. I never see this behaviour while navigating the site, but I'd say the tests fail about 30-50% of the time. Running the test again after a failure seems to fix the problem.
If I load the fixtures manually into the development database, the ID that doesn't seem to be found is indeed present inside the tables.
I haven't been able to find much info on people having the same problem... any ideas?
UPDATE: Here are the contents of test_helper.rb
ENV["RAILS_ENV"] = "test"
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
require 'capybara/rails'
class ActiveSupport::TestCase
# Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
#
# Note: You'll currently still have to declare fixtures explicitly in integration tests
# -- they do not yet inherit this setting
fixtures :all
# Add more helper methods to be used by all tests here...
end
# Transactional fixtures do not work with Selenium tests, because Capybara
# uses a separate server thread, which the transactions would be hidden
# from. We hence use DatabaseCleaner to truncate our test database.
DatabaseCleaner.strategy = :truncation
class ActionDispatch::IntegrationTest
# Make the Capybara DSL available in all integration tests
include Capybara::DSL
# Make the Capybara Email DSL available in all integration tests
include Capybara::Email::DSL
# Stop ActiveRecord from wrapping tests in transactions
self.use_transactional_fixtures = false
# Switch to selenium as the default driver for JS support
Capybara.default_driver = :selenium
# Only click on visible links!
Capybara.ignore_hidden_elements = true
teardown do
DatabaseCleaner.clean # Truncate the database
Capybara.reset_sessions! # Forget the (simulated) browser state
Capybara.use_default_driver # Revert Capybara.current_driver to Capybara.default_driver
end
end
UPDATE Here are the results of running the same test 5 times in a row. I ran the tests, waited until they finished, and then immediately ran them again with the command rails test:integration. NOTE: The E and F that are consistent throughout the tests are actually test errors -- I'm working on fixing those. For example, the second test run-through was "correct", but the first showed the spurious error.
..E......E..........F.
.........E..........F.
..E......E..........F.
..E......E..........F.
..E....E.E..........F.
The errors do occur across two separate tables -- they're not trying to find the same record. But it does appear to be only a subset of the tests that have this issue...
UPDATE Here's what the actual error looks like in the test results:
1) Error:
test_browsing_user_snops(BrowseStoriesTest):
ActiveRecord::RecordNotFound: Couldn't find User with id=980190962
/home/myuser/.rvm/gems/ruby-1.9.3-p286/gems/activerecord-3.2.9/lib/active_record/relation/finder_methods.rb:341:in `find_one'
/home/myuser/.rvm/gems/ruby-1.9.3-p286/gems/activerecord-3.2.9/lib/active_record/relation/finder_methods.rb:312:in `find_with_ids'
/home/myuser/.rvm/gems/ruby-1.9.3-p286/gems/activerecord-3.2.9/lib/active_record/relation/finder_methods.rb:107:in `find'
/home/myuser/.rvm/gems/ruby-1.9.3-p286/gems/activerecord-3.2.9/lib/active_record/querying.rb:5:in `find'
/home/myuser/Projects/myproject/app/controllers/users_controller.rb:18:in `show'
...
Notice that the error appears when the controller tries to find a record. The relevant line is actually #user = User.find(params[:id]). It occurs with other models also, not just the users controller and not just the User model.
I'm concerned that there may be a delay during the truncation of data and the speed in which Capybara is driving the web browser which sometimes may result in identity column values starting at an unexpected value (i.e., 7 for record #1 - when you expect 1, because that identity generator has not yet been reset). I have no evidence that this is the case but its my best guess.
Take at look at item #3 at this URL. This is a pretty straight-forward hack, set use_transactional_fixtures to true and monkeypatch ActiveRecord by pasting that code into your test_helper.rb. This could help in eliminating any intermittent disk IO problems that could be a potential problem.
Another thing you can try is restricting your SQLite test database by setting the filename for that database to :memory: in your database.yml file. This should accomplish the same thing as above - eliminate spurious disk IO that may be causing these intermittent issues.

Optimising Rspec Tests to Avoid Repeating Complex Setup Proceedures

So here's my problem:
I am writing unit tests for my Rails models and I have a whole set of examples that each require the same setup in order to run. If I'm not mistaken, the usual way to set things up the same way for multiple RSpec tests is to use a before(:each) block, like this:
describe Model do
before(:each) do
# Complex setup
end
# Examples
end
Unfortunately the set of examples which needs this setup is starting to get rather large, and completing this complex setup proceedure for each and every test takes a long time. I tried doing this:
describe Model do
before(:all) do
# Complex setup
end
# Examples
end
But this method doesn't roll back my setup though after I'm done with it, which causes problems in later tests. What I really want is to do something like this:
describe Model do
around(:all) do |examples|
transaction do
# Complex setup
examples.run
raise ActiveRecord::Rollback
end
end
# Examples
end
RSpec doesn't currently support an around(:all) hook however. Any ideas?
The easiest way to do this would just be to use an after(:all) block to clean up after your tests.

Using Rails model that is already declared

Declaring a class for (fast) testing purposes is great:
require 'fast_helper'
require 'site_search'
class Post; end # This allows not to load the whole Rails env
describe SiteSearch do
it "searches on posts" do
Post.stub_chain(:scoped, :by_term).with("ruby").and_return ["post1", "post2"]
SiteSearch.by_term("ruby").should == ["post1", "post2"]
end
end
The problem with it is that it seems to break autoloading of rails models when the whole suite of specs is run.
The models aren't loaded anymore when the class gets declared before.
There are 4 ways of injecting the unloaded dependencies:
Declare the class (as in example here)
Set/remove const
Stub a wrapper method
Actually load them
I only want to use the 1st one.
The question: keeping the same specs structure, how can I tell rails to actually load the models even though the class is already declared?
In order for your empty class preemption trick to work, you must have set your app config.cache_classes = false, hence eager loading is not happening unless you call
Rails.application.eager_load!
When running the whole test suite you need to make sure classes preload, then empty redefinition should have no effect.
Now the question is how you control that it is run only if the full test suite is called. The honest answer is I don't know, but you can surely control it from the environment. Somewhere in your rspec helpers you initialize rails, update it to something like:
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
Rails.application.eager_load! unless ENV["FORCE_EAGER_LOAD"].blank?
and then call rspec on full suite as:
FORCE_EAGER_LOAD=t rspec

Rails3: Unit testing a library doesn't load the fixtures

I added a unit test manually to test a library I'm building. I have some fixtures which are tested in their corresponding models without any issues.
The test case inherits from ActiveSupport::TestCase which runs fixtures :all.
require 'test_helper'
require 'mylib'
class MyLibTest < ActiveSupport::TestCase
#user = User.find(1)
test "Setup" do
assert_equal(#user.id, 1)
end
end
Even though user = User.find(1) works fine in every test that is actually for a model, this test raises an exception: /Library/Ruby/Gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/relation/finder_methods.rb:287:in 'find_one': Couldn't find User with ID=1 (ActiveRecord::RecordNotFound)
I have tried moving fixtures :all into the MyLibTest-class (suggested in another post) but with no difference.
How can I make my test work?
EDIT
I found out what was wrong, and it wasn't at all related to the fixtures. I wanted to use the same User object in several test cases so I added #user = User.find inside the class, but not inside a testcase. When I realized this and tried putting it in a test case, everything worked. Awkward :)
I can't be too sure exactly whats up without seeing how your Fixtures file is set up, but try these:
Try moving "fixtures :all" to test_helper.rb.
In the fixtures files, are you setting "id:" on each record? If not, the id is actually not going to be simply incremental - it is created based on the name of the fixture. Try using "User.first" instead of User.find(1). If you need the id to be 1, then set "id: 1" on the fixture.
Hope this helps.

Rails unit testing doesn't load fixtures

rake test:units fails in my current application, because the needed data of the fixtures is missing.
If I'm loading the fixtures manually via rake db:fixtures:load RAILS_ENV=test the unit tests are working, but rake purges the test database.
My test_helper includes fixtures :all and my tests are inheriting from it - but the fixtures are simply not loading.
I'm kind of clueless at the moment and could really need some help!
I've tried a lot and I think it has to do with some environment settings or plugins used in this project. Does anyone know where to read about which files are loaded for the testing environment?
Put the call to fixtures :all in your test class, not the super class (test_helper). My guess is that initialization of the super class isn't working the way you're expecting and that fixtures :all isn't be called. Perhaps try putting the call in the initialize method of test_helper.
My test/test_helper.rb looks like this:
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
class ActiveSupport::TestCase
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
fixtures :all
# Add more helper methods to be used by all tests here...
end
I finally found the problem, although the solutions is kind of hacky.
One plugin is relying that there is some data in the database, at least one row. So what happened was:
rake loads database schema
rake tries to load environment
environment includes plugin
plugin loading fails because of missing at least one row
no fixtures are loaded
The hacky solution is: put the needed data directly into schema and not into a fixtures, because it's loaded to late.
I'll search for a more convenient solution and will update this answer if I found one.
Another approach is to write your own custom rake task for testing.
For example:
task :test_units do
RAILS_ENV = 'test' # Force the environment to test
puts "Recreate the test database"
Rake::Task['db:test:prepare'].invoke
puts "Seed the database with fixtures"
Rake::Task['db:fixtures:load'].invoke
puts "Executing Unit Tests"
Rake::Task['test:units'].prerequisites.clear
Rake::Task['test:units'].invoke
end
My problem is forgot to put "require 'test_helper'" at the head.
eg.
require 'test_helper'
class AdminUserTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end
I had the same problem. Or rather, the problem was that my fixtures were not current with the database schema. Instead of throwing an exception, rails just used the test database as a fallback solution. Most iffy.

Resources