Rails fixtures dumping all data in the db - ruby-on-rails

I've been in Factory land for the past few years and have decided to come back to fixtures but am running into a problem.
In my test_helper.rb I have
class ActiveSupport::TestCase
fixtures :all
end
Then in an individual test case I might be doing something like users(:one) however the data for users(:two) and the data for other tables that I am not calling on seems to be present in the test database.
So, is this the expected behavior? I have a hard to believing it is, seems strange from an isolated testing perspective.

This happens because you load all the fixtures at once from fixtures :all statement. A rule of thumb in tests is to load only the required data for a given test (not all). This also could slow down your test running time.
If you want to load only the selected fixtures you could do
fixtures :<fixture name>
Ex:
fixtures :users
Read more about fixtures.
One more thing, do you have a particular reason to comeback to fixtures. Factories are the way to go; they allow you do organize your test data cleanly.
Read more about factories.

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.

in my rspec test suite, how can I populate a static table with config data my app uses?

So far I use rspec and factories only, not fixtures or seed data. But now I need a (static) Coupon table with a dozen or so records of config data to be available to my app when running my integration tests (rspec + capybara, not cuke).
My rspec tests have a FactoryGirl factory that creates a Store.
In my app, when a Store is initialized by init_store(coupon_code), a coupon code is passed in that turns on/off various features (by looking up the settings for that coupon in Coupon table).
So, when I create a Store factory, I also need to specify (via trait?) a coupon code. Then, down in my model, the method that initializes a store will use the coupon code to lookup a bunch of settings in the Coupon table. (Each coupon has about 20 attributes that turn on/off various Store features.)
My coupons.yml file contains the actual coupon table data used by my app. Now I need to make them available to the app when tests are being run. (Don't care about 'brittle', this table doesn't change.)
Having not used fixtures OR seed data with rspec, I'm not sure how/where to put my coupons.yml file and how to make the coupons 'load' into the test Coupon table so they are available to my app while tests are running.
Do I pre-populate seed data with coupons.yml? If so, how can I have that get loaded into my test database each time my tests run?
Or, should I use fixtures?
Either way, where should coupons.yml go and how do I load it (in the Store factories? in one of the specs? In specs_helper?)
When I search for stuff like "using fixtures and factories" or "using seed data with rspec" all I find is the advice "use factories" and yeah, I get that. But for this particular situation I need both, so any help would be appreciated.
I do something like this, and I use seeds rather than fixtures (though I'm sure you could use fixtures too).
In spec_helper.rb
RSpec.configure do |config|
...
config.before :suite do
DatabaseCleaner.strategy = :truncation, {:except => %w[table1 table2]}
DatabaseCleaner.clean_with :truncation
Rails.application.load_seed
end
...
end
So the DatabaseCleaner.strategy line is telling database_cleaner to clean everything except the array of tables you pass it.
After setting the strategy, you simply call Rails.application.load_seed to load the seed data.
Hope that helps, let me know if you have any questions.

Is it possible to load fixtures after setup?

In my Rails application I'm using PostgreSQL schemas and not every schema contains all tables. (Especially the default path does not) This leads to some problems with fixture loading in Test::Unit.
I'm setting the schema search path in setup, but if I enable fixtures using fixtures they get loaded before my setup modifies the search path and the fixtures cannot be loaded.
class ActiveSupport::TestCase
fixtures :model1, :model2 #Fixtures are loaded before setup below
setup do
# Setup schema search path
end
end
Is it possible to delay loading of the fixtures until after setup or to modify the database connection before the fixtures get loaded? (Note: Not all tests work on the same search path!)
Some background: The existing tests are from an older version without different schemas, so I don't want to rewrite all test just yet using some other means of test data generation, although for future tests I'll definitly switch to factory_girl.

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.

Handling Non-User data in Rails tests

We have a ton of non-user data: 500 product types that haven't changed in 20 years, 200GB of geospatial data that never changes (Queens is always 40.73N/73.82W)... etc. It's the same in Development and Production. It should be the same in Test, but the during testing Rails empties all of the test tables, and it takes a ton of time to re-populate during testing.
What is the Rails way to partition this non-user data so that it doesn't have to be repopulated in Test?
The documentation for this is found in the Fixtures class. (Search for "Transactional fixtures" on that page.)
The example they give starts out like this:
class FooTest < ActiveSupport::TestCase
self.use_transactional_fixtures = true
self.use_instantiated_fixtures = false
...
One of the projects I work on uses a test database with zero fixtures, so we just define this globally in test_helper.rb.
class ActiveSupport::TestCase
self.use_transactional_fixtures = true
self.use_instantiated_fixtures = false
end
jdl shows you the settings to use for enabling and disabling transactional_fixtures, you should be able to set:
# Use self in ActiveSupport::TestCase if you don't have a config block
# in test/test_helper.rb
#
config.use_transactional_fixtures = false
And stop Rails from trying to load fixtures before each run of your tests. The downside is you can't assume all your fixtures are loaded into the DB.
Real Answer
You have too much data in your fixtures. The Railsy thing to do it load only the necessary data in test fixtures; You do not need 200G of geospatial data (what freaking dataset is that anyway? that sounds way too big), and you probably don't need all 500 products.
Tests are for your code, so only include the few geospatial points you need for a test, or only include a few products with unique properties. Keep the DB light enough to be loaded quickly.
Your test fixtures should be separate from you application bootstrapping data, take advantage of that.
Real Real Answer
Don't touch the database in your tests (or, touch it very little). The database is slow and fixtures are difficult or impossible to maintain well. Instead, try using a stubbing framework like Mocha or flexmock. It's faster, and makes your tests read in a clearer manner. Tests are for code, you can stub the database out of the situation and trust that ActiveRecord works (because...it's tested too! :-).
If you do choose to stick with fixtures, I recommend using factory_girl instead of the built in Rails fixtures. You'll have a much better starting point.

Resources