11

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
Vardarac
  • 563
  • 2
  • 17

3 Answers3

2

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.

David Hoelzer
  • 15,862
  • 4
  • 48
  • 67
2

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.

Wally Altman
  • 3,535
  • 3
  • 25
  • 33
0

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.

Community
  • 1
  • 1
creativereason
  • 1,524
  • 1
  • 11
  • 20
  • Trying these causes different errors. The first causes an error stating that a relation doesn't exist, while the second results in an error stating that the test database is not configured. The database yml is not responsible for this, since it was working prior to changes I'm making in my Rakefile. I'll post these changes above. – Vardarac May 27 '15 at 19:30