如何为Rails rspectesting准备testing数据库而不运行rake spec?

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

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

如何运行raketesting数据库设置任务/代码而不运行rake spec

除了能够在一个文件子集上运行rspec外,我还使用specjour将这些规范分布在多个内核中(还没有成功将它们分布到整个局域网上),但是我看到了和运行rspec直接:在specjour工作之前,我需要在每个testing数据库(假设是两个内核)上运行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有这个条目进行testing(对于平行testing的gem是常见的):

 test: adapter: postgresql encoding: unicode database: test<%=ENV['TEST_ENV_NUMBER']%> username: user password: 

parallel_tests似乎正确地设置了它的数据库,但是我们的许多specs失败了。

我还应该提到,运行specjour prepare导致Postgreslogging错误,它找不到数据库,但它创build它们(没有表)。 在随后的运行中,不会logging错误,也不会创build表。 我的整个问题可能只是一个prepare的bug,所以我在github上报告了它。

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

我在工作中设置了CI系统也有类似的问题,于是我逐步build立了一个系统来处理这个问题。 这可能不是最好的解决scheme,但对我而言,这种方法很有用,而且我一直在寻找更好的方法去做事情。

我有一个testing数据库,我需要安装,但也需要加载我们的testing工作的种子数据。

排除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 

这只是一个例子,并且针对我们的情况,所以你需要弄清楚需要做些什么来获得你的testing数据库设置,但是使用rake的–trace选项很容易确定。

此外,如果您发现testing设置耗时过长(如我们的情况),您也可以将数据库转储为.sql格式,并将testing数据库直接导入到MySQL中加载。 我们以这种方式节省了几分钟的testing数据库设置。 我没有在这里显示,因为它使事情变得复杂化 – 它需要正确生成而不会过时等等。

HTH

我会build议删除您的testing数据库,然后重新创build并迁移:

 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指出:

一个简单的解决scheme是运行rake db:test:prepare

但是,如果您使用的是PostgreSQL,这将无法正常工作,因为rails环境会被加载,从而打开数据库连接。 这导致prepare调用失败,因为数据库不能被删除。 棘手的事情。

所提供的解决scheme都需要加载Rails环境,在大多数情况下,由于非常大的开销和非常低的速度,这并不是期望的行为。 DatabaseCleaner Gem也相当慢,它增加了另一个依赖到你的应用程序。

经过几个月的烦恼和烦恼,由于原因见上文,我终于find了以下解决scheme,正是我所需要的。 这很好,简单而快捷。 在spec_helper.rb

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

最好的部分是:它只会清除你已经有效触及的那些表格(未触及过的模型将不会被加载,因此不会出现在subclasses ,也是testing之前不起作用的原因)。 此外,它在testing后执行,所以(希望)绿色的点将立即出现。

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

编辑

看到这个答案已经得到了一些stream行,我想编辑它的完整性:如果你想清除所有的表,即使没有触及,你应该能够做一些像下面的“黑客”。

骇客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 +,最好的解决scheme是简单地添加ActiveRecord::Migration.maintain_test_schema! 在你的rails_helper之后require 'rspec/rails'

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

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" 

这与Leviathan的答案非常相似,加上播种testing数据库,如

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

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

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