如何在不运行 rake spec 的情况下为 Rails rspec 测试准备测试数据库?

Posted

技术标签:

【中文标题】如何在不运行 rake spec 的情况下为 Rails rspec 测试准备测试数据库?【英文标题】:How do I prepare test database(s) for Rails rspec tests without running rake spec? 【发布时间】:2011-08-20 10:37:30 【问题描述】:

经过重大故障排除后,我发现我需要运行一次rake spec(我可以使用 control-c 中止),然后才能直接运行 rspec(例如,在我们规范的一个子集上)。我们正在运行 Rails 3.0.7 和 RSpec 2.5.0。

显然,rake 正在运行一些重要的数据库设置任务/代码(我们在根级 rails Rakefile 和可能的其他地方有自定义代码)。

如何在不运行 rake spec 的情况下运行 rake 测试数据库设置任务/代码?

除了能够在文件子集上运行 rspec 之外,我还使用 specjour 将我们的规范传播到多个内核(尚未成功将它们传播到 LAN 中),但我看到了相同的直接运行 rspec 的行为:在 specjour 工作之前,我需要在每个测试数据库(假设两个核心)上运行 rake spec

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中的一个bug,所以我在github上报告了。

我认为我可以通过在 .specjour/hooks.rb 中设置 Specjour::Configuration.prepare 在每个 specjour 测试数据库上运行任意代码,所以如果有任何 rake 任务或我需要运行的其他代码,它可能在那里工作。

【问题讨论】:

【参考方案1】:

我首先删除了我的测试数据库 rake db:drop RAILS_ENV=test

在尝试创建新的测试数据库时,我遇到了一个问题,因为我的用户帐户与拥有数据库的帐户不同,因此我在 PostgreSQL 中创建了数据库。

在命令提示符中键入psql,然后运行以下命令来创建一个使用您自己以外的帐户的测试数据库。 CREATE DATABASE your_database_name OWNER your_db_owner;

然后在测试环境中运行您的迁移。 rake db:migrate RAILS_ENV=test

【讨论】:

【参考方案2】:

似乎在 Rails 4.1+ 中,最好的解决方案是在您的 rails_helper 中添加 ActiveRecord::Migration.maintain_test_schema! require 'rspec/rails' 之后。

即您不必再担心必须准备数据库了。

https://relishapp.com/rspec/rspec-rails/docs/upgrade#pending-migration-checks

【讨论】:

【参考方案3】:

我建议删除您的测试数据库,然后重新创建并迁移:

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 调用失败,因为无法删除数据库。棘手的事情。

【讨论】:

一个更简单的解决方案是运行rake db:test:prepare 使用 Postgres 运行 rake db:test:prepare 没有问题。您一定是出于其他原因看到您的问题。 看起来 rake db:test:prepare 在 Rails 4 中已被弃用。 你可以像这样链接 rake 任务:bundle exec rake db:drop db:create db:schema:load RAILS_ENV=test rake db:test:prepare 仍然适用于 rails 5 (& 使用 PostgreSQL 10.3)【参考方案4】:

提供的解决方案都需要加载 Rails 环境,在大多数情况下,由于开销非常大且速度非常低,这不是所需的行为。 DatabaseCleaner gem 也很慢,它给你的应用增加了另一个依赖。

由于上述原因,经过数月的懊恼和烦恼,我终于找到了以下解决方案,正是我所需要的。它很好,简单而且快速。在spec_helper.rb:

config.after :all do
  ActiveRecord::Base.subclasses.each(&:delete_all)
end

最好的部分是:它只会清除那些你有效接触过的表格(未接触过的模型不会被加载,因此不会出现在subclasses,这也是这样做的原因't work before 测试)。此外,它在测试后执行,因此(希望)绿点会立即出现。

唯一的缺点是,如果您在运行测试之前有一个脏数据库,它将不会被清理。但我怀疑这是一个主要问题,因为外部测试通常不会触及测试数据库。

编辑

看到这个答案已经获得了一些人气,我想编辑它以保持完整性:如果你想清除 所有 表,即使是那些没有被触及的,你应该能够做类似的事情下文中的“黑客”。

Hack 1 - 为 subclasses 方法预加载所有模型

在调用subclasses之前评估一下:

Dir[Rails.root.join("app", "models", "**", "*.rb")].each(&method(:require))

请注意,此方法可能需要一些时间!

Hack 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

【讨论】:

整洁!这很有用。【参考方案5】:

在 Spring 化的 Rails 4 应用程序中,我的 bin/setup 通常被扩充为包含

puts "\n== Preparing test database =="
system "RAILS_ENV=test bin/rake db:setup"

这与leviathan's answer 非常相似,加上种子测试数据库,如

rake db:setup # 创建数据库,加载模式,并使用种子数据进行初始化 (使用 db:reset 也可以先删除数据库)

正如评论中提到的,如果我们想先删除数据库,rake db:reset 就是这样做的。

我还发现,与 rake db:test:prepare 相比,这提供了更多反馈。

【讨论】:

【参考方案6】:

我在工作中设置 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 进行加载。通过这种方式,我们可以节省几分钟的测试数据库设置。我没有在这里展示它,因为它使事情变得非常复杂——它需要正确生成而不会过时等等。

HTH

【讨论】:

是的,我已经用 --trace 运行了 rake spec 并尝试在我的 prepare specjour 挂钩中复制它的一些任务,但它还没有工作。我可能会编写一个完全独立的 rake 任务来进行设置,但这是我希望避免的另一个步骤。

以上是关于如何在不运行 rake spec 的情况下为 Rails rspec 测试准备测试数据库?的主要内容,如果未能解决你的问题,请参考以下文章

附加到 rake db:seed in rails 并在不复制数据的情况下运行它

如何在不影响 SYSTEM/IE 代理的情况下为 Webbrowser Control 设置代理

如何使 rake 任务在 dev 以外的环境中运行?

如何在不使用 @Composable 注释的情况下为撰写函数创建扩展?

如何在不重新启动 apache 的情况下为站点重新加载 apache 配置?

如何在不限制其维度的情况下为函数参数指定类型?