web-dev-qa-db-fra.com

Comment préparer une ou plusieurs bases de données de test pour Rails rspec teste sans exécuter rake spec?

Après un important dépannage, j'ai compris que je devais exécuter rake spec une fois (je peux abandonner avec control-c) avant de pouvoir exécuter directement rspec (par exemple, sur un sous-ensemble de nos spécifications). Nous courons Rails 3.0.7 et RSpec 2.5.0.

Clairement, rake exécute d'importantes tâches/codes d'installation de la base de données (nous avons du code personnalisé au niveau racine Rails Rakefile et éventuellement d'autres emplacements).

Comment puis-je exécuter les tâches/le code de configuration de la base de données rake test sans exécuter rake spec?

En plus de pouvoir exécuter rspec sur un sous-ensemble de fichiers, j'utilise specjour pour étendre nos spécifications sur plusieurs cœurs (je n'ai pas encore réussi à les diffuser sur le réseau local), mais je vois le même comportement que pour exécuter directement rspec: je dois exécuter rake spec sur chaque base de données de test (en supposant deux cœurs) avant que specjour ne fonctionne:

rake spec TEST_ENV_NUMBER=1
control-c (after tests start)
rake spec TEST_ENV_NUMBER=2
control-c (after tests start)
specjour

Remarque: mon config/database.yml a cette entrée pour test (comme il est courant pour les gems de test en parallèle):

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

parallel_tests semble configurer correctement ses bases de données, mais bon nombre de nos spécifications échouent.

Je devrais aussi mentionner que courir specjour prepare permet à Postgres de consigner les erreurs qui empêchent de trouver les bases de données, mais il les crée (sans tables). Lors d'une exécution ultérieure, aucune erreur n'est consignée, mais aucune table n'est créée. Il est possible que tout mon problème soit simplement un bogue dans prepare, alors je l'ai signalé sur github.

Je pense que je peux exécuter du code arbitraire sur chaque base de données de specjour test en définissant Specjour::Configuration.prepare dans .specjour/hooks.rb, donc s’il ya des tâches de rake ou un autre code que je dois exécuter, cela peut fonctionner ici.

81
gerry3

J'ai eu un problème similaire lors de la configuration du système CI au travail, j'ai donc progressivement mis au point un système pour gérer cela. Ce n'est peut-être pas la meilleure solution, mais cela fonctionne pour moi dans ma situation et je suis toujours à la recherche de meilleures façons de faire les choses.

J'ai une base de données de tests pour laquelle j'avais besoin d'une configuration, mais j'avais également besoin de données initiales chargées pour que nos tests fonctionnent.

L'essentiel du dépannage des tâches de rake consiste à exécuter rake avec l'option --trace pour voir ce qui se passe sous le capot. Lorsque j'ai fait cela, j'ai constaté que l'exécution de rake spec faisait un certain nombre de choses que je pouvais répliquer (ou modifier à ma guise) dans une tâche de rake personnalisée.

Voici un exemple de ce que nous faisons.

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

Ceci est seulement un exemple, et spécifique à notre situation, vous aurez donc besoin de comprendre ce qui doit être fait pour obtenir votre configuration de base de données de test, mais il est assez facile de le déterminer en utilisant l’option --trace de rake.

De plus, si vous trouvez que la configuration du test prend trop de temps (comme dans notre cas), vous pouvez aussi vider la base de données au format .sql et laisser la base de données de test la diriger directement dans mysql pour qu'elle soit chargée. Nous économisons ainsi plusieurs minutes de la configuration de la base de données test. Je ne montre pas cela ici parce que cela complique considérablement les choses - il doit être généré correctement sans devenir obsolète, etc.

HTH

13
edk750

Je recommanderais de supprimer votre base de données de test, puis de la recréer et de migrer:

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

Après ces étapes, vous pouvez exécuter vos spécifications:

bundle exec rspec spec

gerry a noté que:

Une solution plus simple consiste simplement à lancer rake db:test:prepare

Toutefois, si vous utilisez PostgreSQL, cela ne fonctionnera pas car l’environnement Rails est chargé, ce qui ouvre une connexion à une base de données. Ceci entraîne l’échec de l’appel prepare car la base de données ne peut pas être laissé tomber.

150
leviathan

Les solutions fournies nécessitent toutes de charger l’environnement Rails, qui, dans la plupart des cas, n’est pas le comportement souhaité, en raison de la surcharge très importante et de la très faible vitesse. DatabaseCleaner gem est également plutôt lent, et cela ajoute une autre dépendance à votre application.

Après des mois de chagrin et de dépit, grâce aux raisons énoncées plus haut, j’ai finalement trouvé la solution suivante parfaitement adaptée à mes besoins. C'est gentil, simple et rapide. Dans spec_helper.rb:

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

La meilleure partie de ceci est: Cela effacera uniquement les tables que vous avez effectivement touché (Les modèles non modifiés ne seront pas chargés et n'apparaîtront donc pas dans subclasses, ce qui explique également pourquoi pas travailler avant tests). En outre, il est exécuté après les tests, de sorte que les points verts (espérons-le) apparaîtront immédiatement.

Le seul inconvénient est que si vous avez une base de données sale avant d'exécuter des tests, elle ne sera pas nettoyée. Mais je doute qu'il s'agisse d'un problème majeur, car la base de données de tests n'est généralement pas touchée par des tests externes.

Modifier

Considérant que cette réponse a acquis une certaine popularité, je voulais l’éditer de manière exhaustive: si vous voulez effacer les tables toutes, même celles qui ne sont pas touchées, vous devriez pouvoir faire quelque chose comme les "bidouilles". au dessous de.

Hack 1 - précharger tous les modèles pour la méthode subclasses

Evaluez ceci avant d'appeler subclasses:

Dir[Rails.root.join("app", "models", "**", "*.rb")].each(&method(:require))

Notez que cette méthode peut prendre un certain temps!

Hack 2 - tronquer manuellement les tables

ActiveRecord::Base.connection.tables.keep_if{ |x| x != 'schema_migrations' }

vous obtiendrez tous les noms de table, avec ceux que vous pouvez faire quelque chose comme:

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

Il semble que dans Rails 4.1+, la meilleure solution consiste simplement à ajouter ActiveRecord::Migration.maintain_test_schema! dans votre Rails_helper après require 'rspec/Rails'.

c'est-à-dire que vous n'avez plus à vous soucier de la préparation de la base de données.

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

6
John Morales

Dans un print-ified Rails 4 app, mon bin/setup est généralement augmenté pour contenir

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

Ceci est très similaire à réponse de leviathan , plus l'ensemencement du test DB, comme

rake db:setup # Créer la base de données, charger le schéma et initialiser avec les données de départ
(utilisez
db:reset pour supprimer également la base de données en premier)

Comme le commentaire le mentionne, si nous voulons supprimer le DB en premier, rake db:reset fait juste cela.

Je trouve également que cela fournit plus de commentaires par rapport à rake db:test:prepare.

3
Marius Butuc