My legacy code is using DatabaseCleaner which wipes out database after each test being done. However it also wipes out loaded fixtures data and causes some tests which are using fixtures to fail, something like:
ActiveRecord::RecordNotFound Exception: Couldn't find Country with 'id'=593363170
To make them work together, how can I rewrite all fixtures back to database during test? (I mean not rake tasks)
Solution:
Set self.use_transactional_fixtures = false
at the beginning of the file before which you want to reload fixtures.
Or use use_transactional_tests in Rails 5.
Do not set it in spec_helper as it applies globally so conflicts with DatabaseCleaner.
Related
Work is transitioning from Rails 3 to Rails 4. Everything seems to be running more or less smoothly on the development side, but testing results in a multitude of varying failures when all suites - units, functionals, and integration - are run in one go with rake:test.
What's interesting is that running each of these suites individually produces no failures. This strongly suggests that the database is not resetting for our tests in Rails 4 quite the way it was in Rails 3.
I've tried overriding the rake:test task to execute db:test:prepare prior to running each suite, but this apparently doesn't do what I think it does, or rather, what I want it to do, which is to work with a fresh set of data for each test and therefore succeed or fail independently of every other test - the way it (was or should have been) in our Rails 3 setup.
Why is this happening? How might I fix it?
(Note that the testing framework is vanilla Rails with fixtures and the like. The testing failures we're getting are usually foreign key errors or failures for data to change in expected ways that don't show up when the DB is reset before a testing suite.)
EDIT:
Here are the changes I'm making to my Rakefile. The idea is to get rake:test to perform as it would in Rails 3, with the exception of properly resetting the database between unit, functional, and integration suites.
# /Rakefile
task :test => [];
Rake::Task[:test].clear
task :test do
puts Rails.env
if Rails.env == "test"
puts "units"
# Rake::Task["db:drop"].execute - results in 'test database not configured' error
# Rake::Task["db:reset"].execute - results in 'relation 'table' does not exist' error
Rake::Task["db:create"].execute
Rake::Task["db:migrate"].execute
Rake::Task["test:units"].execute
puts "functionals"
Rake::Task["db:schema:load"].execute
Rake::Task["test:functionals"].execute
puts "integration"
Rake::Task["db:schema:load"].execute
Rake::Task["test:integration"].execute
end
end
EDIT 2:
Rails version 4.1.8
Here is my test_helper. I've omitted helper methods that don't work with our data since I'm probably not allowed to share them and they're irrelevant to how the testing database is loaded.
Note also that we have a custom schema.rb from which the testing data are generated, as well, since trying to create them from migrations does not support some of the custom functionality/behavior we want in the end product, like materialized views. Redacted stuff will be enclosed in <<>>.
#test_helper.rb
ENV["RAILS_ENV"] = "test"
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
include ActionDispatch::TestProcess
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
set_fixture_class <<cached_object>>: ResultsDb::<<DesiredObjectClass>>Cache
fixtures :all
# Add more helper methods to be used by all tests here...
def reload_contig(contig_sym)
contig = contigs(contig_sym)
src = contig.src
new_tig = Contig.new(
org: contig.org,
version: contig.version,
size: contig.size,
sha1: contig.sha1,
active: contig.active,
src_id: src.id,
src_type: contig.src_type)
new_tig.id = contig.id
contig.destroy
unless new_tig.save
raise new_tig.errors.full_messages.inspect
end
src.contigs << new_tig
src.save
new_tig
end
def reload_coord(coord_sym)
coord = coords(coord_sym)
new_coord = Coord.new(
:mapped_id => coord.mapped_id,
:mapped_type => coord.mapped_type,
:contig_id => coord.contig_id,
:contig_type => coord.contig_type,
:start => coord.start,
:stop => coord.stop,
:fwd_strand => coord.fwd_strand
)
new_coord.id = coord.id
coord.destroy
new_coord.save!
end
end
You might have a look at the DatabaseCleaner gem that is specifically designed to assist you with database consistency and cleanliness during testing in Rails.
I've only run into problems when my code bypasses ActiveRecord and manipulates the data directly. In all other cases this gem has worked very well for me for testing.
Assuming you are currently on Rails 4.0, your current approach is about the best you can do if you can't add the database_cleaner gem. (I would suggest that you prefer db:schema:load to db:migrate throughout—there's no reason ever to run through all the migrations from the start in the test environment, unless you've done something unusual with them.) But once you get to Rails 4.1 or higher, you've got a better solution available.
Starting with Rails 4.1, management of the test database schema was automated, and rake db:test:prepare was deprecated. If you regenerate your test_helper.rb at this point, everything should work magically.
Have you tried adding either of these in your overriding of rake:test?
rake db:reset db:migrate
or
rake db:drop db:create db:migrate
The question seems similar to this one. I know I've always built my own rake db:recreate that does this sort of thing for test data.
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.
Today, I use the factory_girl instead of the rails fixtures, but i get a problem:
After I run the command "spec spec" done, the data resets to the fixtures, who can tell me the answer?
thank you!
If you intend to use both factories and fixtures in your project and not running them through the rake tasks: eg rake spec, you will need to make sure that you are doing removal of the values from the db by hand. its likely what you are doing is just grabbing an old record in the database and you think the data is being reset. You can verify this by using puts inside your spec to trace the number of records in the db.
puts MyRecord.count
You can clear values in an after or before block.
before(:each) do
Factory(:my_model)
end
after(:each) do
MyModel.delete_all
end
if you intend to use this model or factory in other spec files, you can add these to global before and after blocks in the spec helper.
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.
In a Rails application, I need a table in my database to contain constant data.
This table content is not intended to change for the moment but I do not want to put the content in the code, to be able to change it whenever needed.
I tried filling this table in the migration that created it, but this does not seem to work with the test environment and breaks my unit tests. In test environment, my model is never able to return any value while it is ok in my development environment.
Is there a way to fill that database correctly even in test environment ? Is there another way of handling these kind of data that should not be in code ?
edit
Thanks all for your answers and especially Vlad R for explaining the problem.
I now understand why my data are not loaded in test. This is because the test environment uses the db:load rake command which directly loads the schema instead of running the migrations. Having put my values in the migration only and not in the schema, these values are not loaded for test.
What you are probably observing is that the test framework is not running the migrations (db:migrate), but loading db/schema.rb directly (db:load) instead.
You have two options:
continue to use the migration for production and development; for the test environment, add your constant data to the corresponding yml files in db/fixtures
leave the existing db/fixtures files untouched, and create another set of yml files (containing the constant data) in the same vein as db/fixtures, but usable by both test and production/development environments when doing a rake db:load schema initialization
To cover those scenarios that use db:load (instead of db:migrate - e.g. test, bringing up a new database on a new development machine using the faster db:load instead of db:migrate, etc.) is create a drop-in rakefile in RAILS_APP/lib/tasks to augment the db:load task by loading your constant intialization data from "seed" yml files (one for each model) into the database.
Use the db:seed rake task as an example. Put your seed data in db/seeds/.yml
#the command is: rake:db:load
namespace :db do
desc 'Initialize data from YAML.'
task :load => :environment do
require 'active_record/fixtures'
Dir.glob(RAILS_ROOT + '/db/seeds/*.yml').each do |file|
Fixtures.create_fixtures('db/seeds', File.basename(file, '.*'))
end
end
end
To cover the incremental scenarios (db:migrate), define one migration that does the same thing as the task defined above.
If your seed data ever changes, you will need to add another migration to remove the old seed data and load the new one instead, which may be non-trivial in case of foreign-key dependencies etc.
Take a look at my article on loading seed data.
There's a number of ways to do this. I like a rake task called db:populate which lets you specify your fixed data in normal ActiveRecord create statements. For getting the data into tests, I've just be loading this populate file in my test_helper. However, I think I am going to switch to a test database that already has the seed data populated.
There's also plugin called SeedFu that helps with this problem.
Whatever you do, I recommend against using fixtures for this, because they don't validate your data, so it's very easy to create invalid records.