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

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

很明显,rake 正在运行一些重要的数据库设置任务/代码(我们在根级别 RailsRakefile 中有自定义代码,可能还有其他地方)。

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

除了能够在一个子集文件上运行 rspec,我还使用 (法语)将我们的 specs 分散到多个核(还没有成功地将它们分散到局域网中) ,但是我看到了直接运行 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:

Par _ test 似乎正确地设置了它的数据库,但是我们的许多规范都失败了。

我还应该提到,运行 specjour prepare会导致 Postgres 记录无法找到数据库的错误,但它会创建这些错误(没有表)。在随后的运行中,不会记录任何错误,也不会创建任何表。我的整个问题可能只是 prepare中的一个 bug,所以我在 github 上报告了它。

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

117026 次浏览

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.

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.

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.

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.

HTH

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

gerry3 noted that:

A simpler solution is to just run 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 prepare call to fail, because the DB cannot be dropped. Tricky thing.

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. DatabaseCleaner gem is also rather slow, and it adds another dependency to your app.

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:

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 before tests). Also, it executes after the tests, so the (hopefully) green dots will appear right away.

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 all tables, even the ones not touched, you should be able to do something like the "hacks" below.

Hack 1 - pre-loading all models for subclasses method

Evaluate this before calling 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

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

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'.

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

In a spring-ified Rails 4 app, my bin/setup is usually augmented to contain

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
(use
db:reset to also drop the database first)

As the comment mentions, if we want to drop the DB first, rake db:reset does just that.

I also find that this provides more feedback when compared to rake db:test:prepare.

I started by dropping my test database rake db:drop RAILS_ENV=test

when trying to create a new test database I ran into an issue because my user account was not the same as the account that owns the databases so I created the database in PostgreSQL instead.

type psql in the command prompt and then run the below to create a test database that uses an account other than your own. CREATE DATABASE your_database_name OWNER your_db_owner;

then run your migrations in the test environment. rake db:migrate RAILS_ENV=test