web-dev-qa-db-ja.com

rake specを実行せずにRails rspecテスト用のテストデータベースを準備するにはどうすればよいですか?

重要なトラブルシューティングの後、rspecを直接実行する前に(たとえば、仕様のサブセットで)rake specを1回実行する必要があることがわかりました(control-cで中止できます)。 Rails 3.0.7およびRSpec 2.5.0を実行しています。

明らかに、rakeはいくつかの重要なデータベースセットアップタスク/コードを実行しています(ルートレベルにカスタムコードがありますRails Rakefileおよびおそらく他の場所)。

rake spec?を実行せずに、rakeテストデータベースのセットアップタスク/コードを実行するにはどうすればよいですか?

ファイルのサブセットでrspecを実行できることに加えて、 specjour を使用して仕様を複数のコアに分散しています(まだLANに展開することに成功していません)が、 rspecを直接実行する場合と同じ動作:specjourが機能する前に、各テストデータベースで(2つのコアを想定して)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のバグである可能性があるため、githubで報告しました。

.specjour/hooks.rbでSpecjour::Configuration.prepareを設定することで、各specjourテストデータベースで任意のコードを実行できると思うので、実行する必要のあるrakeタスクまたは他のコードがある場合、そこで動作する可能性があります。

81
gerry3

職場で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

13
edk750

テストデータベースを削除してから、再作成して移行することをお勧めします。

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

gerry 次のことに注意してください:

より簡単な解決策は、単にrake db:test:prepare

ただし、PostgreSQLを使用している場合、Rails環境がロードされ、データベース接続が開かれます。これにより、DBができないためprepare呼び出しが失敗します。落とします。

150
leviathan

提供されるソリューションはすべて、Rails環境をロードする必要があります。これは、ほとんどの場合、非常に大きなオーバーヘッドと非常に低速のため、望ましい動作ではありません。DatabaseCleaner gemもかなり遅く、アプリに別の依存関係が追加されます。

上記の理由により、数か月にわたる悔しさと悔しさのおかげで、最終的に次の解決策がまさに必要なものであることがわかりました。素晴らしく、シンプルで高速です。 spec_helper.rb

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

これについての最良の部分は、あなたが効果的に持っているテーブルのみをクリアしますtouched(未処理のモデルはロードされないため、subclassesに表示されません。これもそうしない理由です。動作しませんbefore tests)。また、テスト後に実行されるため、(できれば)緑色のドットがすぐに表示されます。

これの唯一の欠点は、テストを実行する前にダーティデータベースがある場合、クリーンアップされないことです。しかし、テストデータベースは通常、外部のテストから影響を受けないため、これは大きな問題ではないと思います。

編集

この答えが人気を博しているのを見て、完全を期すためにそれを編集したいと思いました:allテーブル、触れられていないテーブルでもクリアしたいなら、「ハック」のようなことができるはずです未満。

ハック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
14
Danyel

Rails 4.1+では、最良の解決策は単にActiveRecord::Migration.maintain_test_schema!の後にRails_helperでrequire 'rspec/Rails'

つまり、もうデータベースを準備する必要について心配する必要はありません。

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

6
John Morales

春に限定されたRails 4アプリでは、私のbin/setupは通常、

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

これは leviathan's answer に非常に似ていますが、テストDBをシードします。

rake db:setup#データベースを作成し、スキーマをロードし、シードデータで初期化する
(use
db:resetを使用して、最初にデータベースを削除する)

コメントが言及しているように、DBを最初に削除したい場合、rake db:resetはまさにそれを行います。

rake db:test:prepareと比較した場合、これはより多くのフィードバックを提供することもわかりました。

3
Marius Butuc