Как подготовить тестовую базу для тестов Rails rspec без использования спецификации rake?

После значительного устранения неполадок, я понял, что мне нужно запустить rake spec один раз (я могу прервать с помощью control-c), прежде чем я могу запустить rspec напрямую (например, в подмножестве наших спецификаций). Мы запускаем Rails 3.0.7 и RSpec 2.5.0.

Очевидно, что rake запускает некоторые важные задачи/код настройки базы данных (у нас есть настраиваемый код в рельсах корневого уровня Rakefile и, возможно, в других местах).

Как запустить задачи/код установки базы данных rake test без запуска rake spec?

Помимо возможности запуска rspec в подмножестве файлов, я использую specjour для распространения наших спецификаций на нескольких ядрах ( еще не успели распространить их по локальной сети), но я вижу то же поведение, что и для прямого запуска 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

Примечание. my config/database.yml имеет эту запись для теста (как это обычно бывает для параллельных тестов):

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

parallel_tests, похоже, правильно настроил свои базы данных, но многие из наших спецификаций терпят неудачу.

Следует также упомянуть, что запуск specjour prepare заставляет Postgres регистрировать ошибки, что он не может найти базы данных, но создает их (без таблиц). При последующем запуске ошибок не регистрируются, но также не создаются таблицы. Возможно, что вся моя проблема - это просто ошибка в prepare, поэтому я сообщил об этом в github.

Я думаю, что я могу запустить произвольный код в каждой тестовой базе данных specjour, установив Specjour::Configuration.prepare в .specjour/hooks.rb, поэтому, если есть какие-либо задачи rake или другой код, который мне нужно запустить, он может работать там.

Ответ 1

У меня была аналогичная проблема с настройкой системы CI на работе, поэтому я постепенно разработал систему для этого. Это может быть не лучшее решение, но оно работает для меня в моей ситуации, и я всегда в поиске лучших способов сделать что-то.

У меня есть тестовая база данных, в которой мне нужна настройка, но также нужны загруженные данные для наших тестов.

Основы поиска и устранения неполадок - это запустить rake с опцией --trace, чтобы увидеть, что происходит под капотом. Когда я это сделал, я обнаружил, что работающий 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

Это только пример и характерная для нашей ситуации, поэтому вам нужно выяснить, что нужно сделать, чтобы получить тестовую настройку db, но ее довольно легко определить с помощью опции -trace rake.

Кроме того, если вы обнаружите, что тестовая установка занимает слишком много времени (как в нашем случае), вы также можете сбросить базу данных в формат .sql и напрямую загрузить тестовую базу данных в mysql для загрузки. Мы сохраняем несколько минут после установки тестового db таким образом. Я не показываю это здесь, потому что он существенно усложняет ситуацию - он должен быть создан правильно, не становясь устаревшим и т.д.

НТН

Ответ 2

Я бы порекомендовал сбросить вашу тестовую базу данных, затем заново создать ее и выполнить миграцию:

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, это не будет работать, потому что загружается среда рельсов, которая открывает соединение с базой данных. Это приводит к сбою вызова prepare, поскольку БД нельзя отбросить. Трудная вещь.

Ответ 3

Все предлагаемые решения требуют загрузки среды Rails, которая в большинстве случаев не является желаемым поведением из-за очень больших накладных расходов и очень низкой скорости. DatabaseCleaner gem также довольно медленный, и он добавляет другую зависимость к вашему приложению.

После долгих огорчений и досады по причинам, приведенным выше, я, наконец, нашел следующее решение именно в том, что мне нужно. Это хорошо, просто и быстро. В spec_helper.rb:

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

Самое приятное в этом: оно очистит только те таблицы, которые вы эффективно коснулись (нетронутые модели не будут загружены и, следовательно, не будут отображаться в subclasses, также причина, почему это не происходит перед). Кроме того, он выполняется после тестов, поэтому зеленые точки (надеюсь) будут отображаться сразу.

Единственным недостатком этого является то, что если у вас есть грязная база данных перед запуском тестов, она не будет очищена. Но я сомневаюсь, что это серьезная проблема, так как тестовая база данных обычно не касается внешних тестов.

Изменить

Увидев, что этот ответ приобрел некоторую популярность, я хотел его отредактировать для полноты: если вы хотите очистить таблицы all, даже те, которые не коснулись, вы должны сделать что-то вроде "хаки" ниже.

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

Ответ 4

Похоже, что в Rails 4.1+ лучшим решением является просто добавить ActiveRecord::Migration.maintain_test_schema! в rails_helper после require 'rspec/rails'.

то есть. вам не нужно беспокоиться о необходимости подготовки базы данных.

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

Ответ 5

В приложении с поддержкой spring Rails 4 мой bin/setup обычно добавляется, чтобы содержать

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

Это очень похоже на ответ leviathan, плюс посев тестовой БД, как

rake db:setup # Создайте базу данных, загрузите схему и инициализируйте данные семени
(используйте db:reset, чтобы сначала удалить базу данных)

Как отмечается в комментарии, если мы хотим сначала сбросить базу данных, rake db:reset сделает именно это.

Я также обнаружил, что это дает больше обратной связи по сравнению с rake db:test:prepare.