Ruby-on-rails 如何在不运行 rake 规范的情况下为 Rails rspec 测试准备测试数据库?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5916126/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I prepare test database(s) for Rails rspec tests without running rake spec?
提问by gerry3
After significant troubleshooting, I figured out that I needed to run rake speconce (I can abort with control-c) before I can run rspec directly (e.g. on a subset of our specs). We are running Rails 3.0.7 and RSpec 2.5.0.
经过重大故障排除后,我发现我需要运行rake spec一次(我可以使用 control-c 中止),然后才能直接运行 rspec(例如,在我们规范的一个子集上)。我们正在运行 Rails 3.0.7 和 RSpec 2.5.0。
Clearly, rake is running some important database setup tasks / code (we have custom code in the root level rails Rakefile and possibly other places).
显然,rake 正在运行一些重要的数据库设置任务/代码(我们在根级 rails Rakefile 和可能的其他地方有自定义代码)。
How can I run the rake test database setup tasks / code without running rake spec?
如何在不运行的情况下运行 rake 测试数据库设置任务/代码rake spec?
In addition to being able to run rspec on a subset of files, I am using specjourto spread our specs across multiple cores (haven't had success with spreading them across the LAN yet), but I see the same behavior as for running rspec directly: I need to run rake specon each test database (assuming two cores) before specjour works:
除了能够在文件子集上运行 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
Note: my config/database.yml has this entry for test (as is common for the parallel testing gems):
注意:我的 config/database.yml 有这个测试条目(对于并行测试 gems 很常见):
test:
adapter: postgresql
encoding: unicode
database: test<%=ENV['TEST_ENV_NUMBER']%>
username: user
password:
parallel_testsseems to set up its databases correctly, but many of our specs fail.
parallel_tests似乎正确设置了它的数据库,但我们的许多规范都失败了。
I should also mention that running specjour preparecauses Postgres to log errors that it can't find the databases, but it creates them (without tables). On a subsequent run, no errors are logged, but also no tables are created. It is possible that my whole issue is simply a bug in prepare, so I reported it on github.
我还应该提到,运行specjour prepare会导致 Postgres 记录无法找到数据库的错误,但它会创建它们(没有表)。在后续运行中,不会记录任何错误,也不会创建任何表。有可能我的整个问题只是 中的一个错误prepare,所以我在 github 上报告了它。
I think that I can run arbitrary code on each specjour test database by setting Specjour::Configuration.preparein .specjour/hooks.rb, so if there's any rake tasks or other code that I need to run, it may work there.
我认为我可以通过Specjour::Configuration.prepare在 .specjour/hooks.rb 中设置在每个 specjour 测试数据库上运行任意代码,所以如果有任何 rake 任务或我需要运行的其他代码,它可能在那里工作。
采纳答案by edk750
I had a similar problem setting up the CI system at work, so I gradually worked up a system to handle this. It may not be the best solution, but it works for me in my situation and I'm always on the lookout for better ways to do things.
我在工作中设置 CI 系统时遇到了类似的问题,所以我逐渐建立了一个系统来处理这个问题。这可能不是最好的解决方案,但它在我的情况下对我有用,而且我一直在寻找更好的做事方式。
I have a test database that I needed setup, but also needed seeded data loaded for our tests to work.
我有一个需要设置的测试数据库,但也需要为我们的测试加载种子数据才能工作。
The basics of troubleshooting rake tasks is to run rake with the --trace option to see what is happening under the hood. When i did this, I found that running rake spec did a number of things that I could replicate (or modify as I saw fit) in a custom rake task.
对 rake 任务进行故障排除的基础是使用 --trace 选项运行 rake 以查看幕后发生的事情。当我这样做时,我发现运行 rake spec 做了很多我可以在自定义 rake 任务中复制(或根据我认为合适的修改)的事情。
Here's an example of what we do.
这是我们所做的一个例子。
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
This is only an example, and specific to our situation, so you'll need to figure out what needs to be done to get your test db setup, but it is quite easy to determine using the --trace option of rake.
这只是一个例子,具体到我们的情况,所以你需要弄清楚需要做什么来设置你的测试数据库,但是使用 rake 的 --trace 选项很容易确定。
Additionally, if you find the test setup is taking too long (as it does in our case), you can also dump the database into .sql format and have the test database pipe it directly into mysql to load. We save several minutes off the test db setup that way. I don't show that here because it complicates things substantially -- it needs to be generated properly without getting stale, etc.
此外,如果您发现测试设置花费的时间太长(就像我们的情况一样),您还可以将数据库转储为 .sql 格式,并让测试数据库将其直接通过管道导入 mysql 以进行加载。我们以这种方式在测试数据库设置中节省了几分钟。我没有在这里展示它,因为它使事情变得非常复杂——它需要正确生成而不会变得陈旧等。
HTH
HTH
回答by leviathan
I would recommend dropping your test database, then re-create it and migrate:
我建议删除您的测试数据库,然后重新创建它并迁移:
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
After these steps you can run your specs:
在这些步骤之后,您可以运行您的规范:
bundle exec rspec spec
gerry3noted that:
gerry3指出:
A simpler solution is to just run
rake db:test:prepare
一个更简单的解决方案是运行
rake db:test:prepare
However, if you're using PostgreSQL this wont work because the rails environment gets loaded, which opens a database connection. This causes the preparecall to fail, because the DB cannot be dropped. Tricky thing.
但是,如果您使用的是 PostgreSQL,这将不起作用,因为 Rails 环境被加载,这会打开一个数据库连接。这会导致prepare调用失败,因为无法删除 DB。棘手的事情。
回答by Danyel
The provided solutions all require to load the Rails environment, which is, in most cases, not the desired behaviour due to very large overhead and very low speed. DatabaseCleanergem is also rather slow, and it adds another dependency to your app.
提供的解决方案都需要加载 Rails 环境,在大多数情况下,由于非常大的开销和非常低的速度,这不是所需的行为。DatabaseCleanergem 也很慢,它会为您的应用程序添加另一个依赖项。
After months of chagrin and vexation thanks to reasons vide supra, I have finally found the following solution to be exactly what I need. It's nice, simple and fast. In spec_helper.rb:
由于上述原因,在几个月的懊恼和烦恼之后,我终于找到了以下解决方案,这正是我所需要的。它很好,简单且快速。在spec_helper.rb:
config.after :all do
ActiveRecord::Base.subclasses.each(&:delete_all)
end
The best part about this is: It will only clear those tables that you have effectively touched(untouched Models will not be loaded and thus not appear in subclasses, also the reason why this doesn't work beforetests). Also, it executes after the tests, so the (hopefully) green dots will appear right away.
最好的部分是:它只会清除您有效触及的那些表(未触及的模型不会被加载,因此不会出现在 中subclasses,这也是测试前这不起作用的原因)。此外,它会在测试后执行,因此(希望)绿点会立即出现。
The only downside to this is that if you have a dirty database before running tests, it will not be cleaned. But I doubt that is a major issue, since the test database is usually not touched from outside tests.
唯一的缺点是如果您在运行测试之前有一个脏数据库,它将不会被清理。但我怀疑这是一个主要问题,因为外部测试通常不会触及测试数据库。
Edit
编辑
Seeing as this answer has gained some popularity, I wanted to edit it for completeness: if you want to clear alltables, even the ones not touched, you should be able to do something like the "hacks" below.
看到这个答案已经获得了一些人气,我想对其进行编辑以确保完整性:如果您想清除所有表格,即使是未触及的表格,您应该能够执行类似于下面的“hacks”的操作。
Hack 1 - pre-loading all models for subclassesmethod
Hack 1 - 为subclasses方法预加载所有模型
Evaluate this before calling subclasses:
在调用之前评估一下subclasses:
Dir[Rails.root.join("app", "models", "**", "*.rb")].each(&method(:require))
Note that this method may take some time!
请注意,此方法可能需要一些时间!
Hack 2 - manually truncating the tables
Hack 2 - 手动截断表格
ActiveRecord::Base.connection.tables.keep_if{ |x| x != 'schema_migrations' }
will get you all table names, with those you can do something like:
将为您提供所有表名,您可以执行以下操作:
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
回答by John Morales
It appears that in Rails 4.1+, the best solution is simply to add ActiveRecord::Migration.maintain_test_schema!in your rails_helper after require 'rspec/rails'.
似乎在 Rails 4.1+ 中,最好的解决方案是ActiveRecord::Migration.maintain_test_schema!在require 'rspec/rails'.
i.e. you don't have to worry about having to prepare the database anymore.
即您不必再担心必须准备数据库。
https://relishapp.com/rspec/rspec-rails/docs/upgrade#pending-migration-checks
https://relishapp.com/rspec/rspec-rails/docs/upgrade#pending-migration-checks
回答by Marius Butuc
In a spring-ified Rails 4 app, my bin/setupis usually augmented to contain
在一个弹簧化的 Rails 4 应用程序中,我bin/setup通常被增强为包含
puts "\n== Preparing test database =="
system "RAILS_ENV=test bin/rake db:setup"
This is very similar to leviathan's answer, plus seeding the test DB, as
这与利维坦的答案非常相似,加上测试数据库的种子,如
rake db:setup# Create the database, load the schema, and initialize with the seed data
(usedb:resetto also drop the database first)
rake db:setup# 创建数据库,加载模式,并用种子数据初始化
(也用于db:reset先删除数据库)
As the comment mentions, if we want to drop the DB first, rake db:resetdoes just that.
正如评论中提到的,如果我们想先删除数据库,rake db:reset就这样做。
I also find that this provides more feedback when compared to rake db:test:prepare.
我还发现,与rake db:test:prepare.

