为什么Rails在测试运行之间不会重置数据库?

7

在Rails代码库中有注释表明测试数据库应在运行之间重置

rake -T

rake test:all                           # Run tests quickly by merging all types and not resetting db
rake test:all:db                        # Run tests quickly, but also reset db

config/database.yml

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:

这对我来说似乎不是这种情况。
我正在使用factory girl生成测试模型,以下是一个示例工厂。
FactoryGirl.define do
  factory :podcast do
    sequence(:title)     { |n| "Podcast #{n}" }
    sequence(:feed_url)  { |n| "http://podcast.com/#{n}" }
  end
end

播客应该有一个唯一的feed_url,因此我在模型中验证其唯一性。

class Podcast < ActiveRecord::Base
  validates :feed_url, uniqueness: true, presence: true
end

test_helper.rb中,我检查所有的工厂。
ENV["RAILS_ENV"] ||= "test"
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
require 'minitest/autorun'

FactoryGirl.lint

我的测试创建了一个播客,又建立了一个同名的播客,然后断言第二个播客是无效的。

require 'test_helper'

describe Podcast do
  describe '#feed_url' do
    it 'must be unique' do
      podcast = create(:podcast)
      new_podcast = build(:podcast, feed_url: podcast.name)

      assert_invalid podcast, :feed_url, 'has already been taken'
    end
  end
end

第一次运行测试时,所有测试都通过且没有错误。 第二次运行测试时,Factory Girl的lint失败,因为播客feed_url已经被占用。 为什么运行之间测试数据库没有被重置?
4个回答

8
我们有一个更复杂的 FactoryGirl 设置,准备了一些基本项目以填充我们的数据库,但我认为您可能可以将此代码直接放入您的 test_helper.rb 中,以确保数据库被清空:
# Destroy all models because they do not get destroyed automatically
(ActiveRecord::Base.connection.tables - %w{schema_migrations}).each do |table_name|
  ActiveRecord::Base.connection.execute "TRUNCATE TABLE #{table_name};"
end

或者,在每次运行之前运行rake db:test:prepare

还有一个可以使用的gem,但我没有任何使用经验:http://rubygems.org/gems/database_cleaner


8
数据库未重置的原因是您在rails提供的数据库事务之外运行测试。 ActiveSupport :: TestCase 类是所有rails测试的基础。 ActiveRecord将每个测试用例添加到此类的事务中。此事务将在每次测试后重置数据库。但是,您不是使用 ActiveSupport :: TestCase 运行测试,而是使用未配置为运行事务的 Minitest :: Spec 运行测试。
最简单的解决方案是将minitest-rails添加到您的Gemfile中,并将test_helper.rb文件中的require从 minitest / autorun 更改为 minitest / rails 。如果您更愿意添加自己的Minitest规范DSL支持,则可以使用这篇文章作为起点。

2
感谢回答问题,使用测试之间的事务回滚与 rdnewman 建议的在运行之间截断数据库相比,是否有优势? - everett1992

2

我接受了rdnewman的答案,因为它不会引入更多的依赖关系。你能为使用database_cleaner提出理由吗? - everett1992
如果rdnewman的答案对您有效,那么我建议您坚持使用它,因为它足够简单。database_cleaner实现了相同的目标,只是它支持更多的ORM和更多的数据库清理策略。 - Chris Gunther

1
如果您正在使用“Rspec”作为单元测试框架。在安装了 gem 'rspec-rails' 后,您将得到一个名为:spec/rails_helper.rb 的配置文件,并在其中找到一个类似于这样的配置:
  # If you're not using ActiveRecord, or you'd prefer not to run each of your
  # examples within a transaction, remove the following line or assign false
  # instead of true.
  config.use_transactional_fixtures = true

这意味着如果为真,则每个测试用例将在单独的事务中运行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接