Как подготовить тестовую базу данных для тестов Rails rspec без запуска rake spec? - PullRequest
80 голосов
/ 06 мая 2011

После значительного устранения неполадок я понял, что мне нужно выполнить 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

Примечание: в моем 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-задачи или другой код, который мне нужно запустить, он может работать там.

Ответы [ 5 ]

148 голосов
/ 22 июня 2012

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

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

14 голосов
/ 04 августа 2013

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

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

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

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

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

Редактировать

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

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
13 голосов
/ 07 мая 2011

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

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

Основы поиска и устранения неисправностей в rake-задачах - это запуск rake с опцией --trace, чтобы увидеть, что происходит под капотом. Когда я сделал это, я обнаружил, что запуск спецификации рейка сделал несколько вещей, которые я мог воспроизвести (или изменить, как мне показалось нужным) в пользовательской задаче рейка.

Вот пример того, что мы делаем.

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

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

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

НТН

6 голосов
/ 20 мая 2015

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

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

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

3 голосов
/ 07 июля 2015

В приложении Rails 4 с пружинным форматом my bin/setup обычно увеличивается до

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

Это очень похоже на ответ Левиафана , плюс заполнение тестовой базы данных, как

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

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

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

...