经过大量的故障排除,我发现我需要运行一次rake spec
(我可以使用 control-c 中止),然后才能直接运行 rspec(例如,在我们的规范子集上)。我们运行的是 Rails 3.0.7 和 RSpec 2.5.0。
显然,rake 正在运行一些重要的数据库设置任务/代码(我们在根级 rails Rakefile 和可能的其他地方都有自定义代码)。
如何在不运行rake spec
的情况下运行 rake 测试数据库设置任务/代码?
除了能够在文件子集上运行 rspec 之外,我还使用 specjour 将我们的规范分布在多个内核上(尚未成功将它们分布到 LAN 上),但我看到了与直接运行 rspec 相同的行为:我需要在每个测试数据库(假设有两个内核)上运行 rake spec
在 specjour 工作之前:
rake spec TEST_ENV_NUMBER=1
control-c (after tests start)
rake spec TEST_ENV_NUMBER=2
control-c (after tests start)
specjour
注意:我的 config/database.yml 有这个用于测试的条目(对于并行测试 gem 来说很常见):
test:
adapter: postgresql
encoding: unicode
database: test<%=ENV['TEST_ENV_NUMBER']%>
username: user
password:
parallel_tests似乎正确设置了其数据库,但是我们的许多规范都失败了。
我还应该提到,运行specjour prepare
会导致 Postgres 记录它找不到数据库的错误,但它会创建它们(没有表)。在后续运行中,不会记录任何错误,但也不会创建表。我的整个问题可能只是prepare
中的一个错误,所以我在 github 上报告了它。
我认为我可以通过在 .specjour/hooks.rb 中设置Specjour::Configuration.prepare
在每个 specjour 测试数据库上运行任意代码,所以如果需要运行任何 rake 任务或其他代码,它可能会在那里工作。
我建议删除您的测试数据库,然后重新创建它并迁移:
bundle exec rake db:drop RAILS_ENV=test
bundle exec rake db:create RAILS_ENV=test
bundle exec rake db:schema:load RAILS_ENV=test
完成这些步骤后,您可以运行您的规范:
bundle exec rspec spec
Gerry3指出:
更简单的解决方案是只运行
rake db:test:prepare
但是,如果您使用的是PostgreSQL,这将不起作用,因为加载了rails环境,这将打开数据库连接。这会导致prepare
调用失败,因为无法删除数据库。棘手的事情。
我在工作中设置CI系统时遇到了类似的问题,所以我逐渐开发了一个系统来处理这个问题。 这可能不是最好的解决方案,但它在我的情况下对我有用,我一直在寻找更好的做事方法。
我有一个需要设置的测试数据库,但也需要加载种子数据才能使我们的测试正常工作。
对 rake 任务进行故障排除的基础知识是使用 --trace 选项运行 rake,以查看幕后发生的事情。 当我这样做时,我发现运行 rake spec 做了很多我可以在自定义 rake 任务中复制(或修改我认为合适的)的事情。
以下是我们工作的一个例子。
desc "Setup test database - drops, loads schema, migrates and seeds the test db"
task :test_db_setup => [:pre_reqs] do
Rails.env = ENV['RAILS_ENV'] = 'test'
Rake::Task['db:drop'].invoke
Rake::Task['db:create'].invoke
result = capture_stdout { Rake::Task['db:schema:load'].invoke }
File.open(File.join(ENV['CC_BUILD_ARTIFACTS'] || 'log', 'schema-load.log'), 'w') { |f| f.write(result) }
Rake::Task['db:seed:load'].invoke
ActiveRecord::Base.establish_connection
Rake::Task['db:migrate'].invoke
end
这只是一个例子,特定于我们的情况,所以你需要弄清楚需要做什么来设置你的测试数据库,但使用 rake 的 --trace 选项很容易确定。
此外,如果您发现测试设置花费的时间太长(就像在我们的例子中一样),您还可以将数据库转储为.sql格式,并让测试数据库将其直接传输到 mysql 中加载。 通过这种方式,我们可以节省几分钟的测试数据库设置。 我在这里没有展示这一点,因为它使事情变得非常复杂——它需要正确生成而不会过时等。
呵呵
提供的解决方案都需要加载 Rails 环境,在大多数情况下,由于开销非常大和速度非常低,这不是所需的行为。 DatabaseCleaner
gem 也相当慢,它会为您的应用添加另一个依赖项。
由于上述原因,经过几个月的懊恼和烦恼,我终于找到了以下解决方案正是我所需要的。它很好,简单,快速。在spec_helper.rb
:
config.after :all do
ActiveRecord::Base.subclasses.each(&:delete_all)
end
最好的部分是:它只会清除您有效接触的那些表(未触及的模型不会被加载,因此不会出现在subclasses
中,这也是在测试之前这不起作用的原因)。此外,它在测试后执行,因此(希望)绿点会立即出现。
这样做的唯一缺点是,如果在运行测试之前有一个脏数据库,则不会对其进行清理。但我怀疑这是一个主要问题,因为测试数据库通常不会从外部测试中触及。
编辑
看到这个答案已经获得了一些流行,我想编辑它以保持完整性:如果你想清除所有表格,即使是那些没有触及的表格,你应该能够做一些类似于下面的"hacks"的事情。
黑客 1 - 为subclasses
方法预加载所有模型
在调用subclasses
之前评估这一点:
Dir[Rails.root.join("app", "models", "**", "*.rb")].each(&method(:require))
请注意,此方法可能需要一些时间!
黑客 2 - 手动截断表
ActiveRecord::Base.connection.tables.keep_if{ |x| x != 'schema_migrations' }
将获取所有表名,您可以使用这些表名执行以下操作:
case ActiveRecord::Base.configurations[Rails.env]["adapter"]
when /^mysql/, /^postgresql/
ActiveRecord::Base.connection.execute("TRUNCATE #{table_name}")
when /^sqlite/
ActiveRecord::Base.connection.execute("DELETE FROM #{table_name}")
ActiveRecord::Base.connection.execute("DELETE FROM sqlite_sequence where name='#{table_name}'")
end
似乎在 Rails 4.1+ 中,最好的解决方案就是在 require 'rspec/rails'
之后在rails_helper中添加ActiveRecord::Migration.maintain_test_schema!
。
也就是说,您不必担心必须再准备数据库。
https://relishapp.com/rspec/rspec-rails/docs/upgrade#pending-migration-checks
在弹簧化的 Rails 4 应用程序中,我的bin/setup
通常会被增强以包含
puts "n== Preparing test database =="
system "RAILS_ENV=test bin/rake db:setup"
这与利维坦的答案非常相似,加上测试数据库的种子,作为
rake db:setup
# 创建数据库,加载模式,并使用种子数据
初始化 (使用db:reset
也先删除数据库)
正如评论中提到的,如果我们想先删除数据库,rake db:reset
就是这样做的。
我还发现,与rake db:test:prepare
相比,这提供了更多的反馈。
我从删除测试数据库开始 rake db:drop RAILS_ENV=test
尝试创建新的测试数据库时,我遇到了一个问题,因为我的用户帐户与拥有数据库的帐户不同,所以我在 PostgreSQL 中创建了数据库。
在命令提示符下键入 psql
,然后运行以下命令以创建使用你自己帐户以外的帐户的测试数据库。 CREATE DATABASE your_database_name OWNER your_db_owner;
然后在测试环境中运行迁移。 rake db:migrate RAILS_ENV=test