Wie bereite ich Testdatenbanken für Rails-Rspec-Tests vor, ohne die Rake-Spezifikation auszuführen?

82

Nach einer umfangreichen Fehlerbehebung stellte ich fest, dass ich rake speceinmal ausführen musste (ich kann mit control-c abbrechen), bevor ich rspec direkt ausführen konnte (z. B. für eine Teilmenge unserer Spezifikationen). Wir verwenden Rails 3.0.7 und RSpec 2.5.0.

Es ist klar, dass Rake einige wichtige Datenbank-Setup-Aufgaben / -Code ausführt (wir haben benutzerdefinierten Code in den Rakefile-Rails der Root-Ebene und möglicherweise an anderen Stellen).

Wie kann ich die Setup-Aufgaben / den Code für die Rake-Test-Datenbank ausführen, ohne sie auszuführen rake spec?

Zusätzlich zur Möglichkeit, rspec für eine Teilmenge von Dateien auszuführen , verwende ich specjour , um unsere Spezifikationen auf mehrere Kerne zu verteilen (ich hatte noch keinen Erfolg damit, sie über das LAN zu verteilen), aber ich sehe dasselbe Verhalten wie beim Ausführen von rspec direkt: Ich muss rake specauf jeder Testdatenbank (unter der Annahme von zwei Kernen) ausgeführt werden, bevor specjour funktioniert:

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

Hinweis: Meine config / database.yml hat diesen Eintrag zum Testen (wie es für die Edelsteine ​​des parallelen Testens üblich ist):

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

parallel_tests scheint seine Datenbanken korrekt einzurichten, aber viele unserer Spezifikationen schlagen fehl.

Ich sollte auch erwähnen, dass das Ausführen specjour preparedazu führt, dass Postgres Fehler protokolliert, bei denen die Datenbanken nicht gefunden werden können, diese jedoch erstellt werden (ohne Tabellen). Bei einem nachfolgenden Lauf werden keine Fehler protokolliert, aber auch keine Tabellen erstellt. Es ist möglich, dass mein gesamtes Problem einfach ein Fehler ist prepare, also habe ich es auf Github gemeldet.

Ich denke, dass ich beliebigen Code in jeder Specjour-Testdatenbank ausführen kann, indem ich Specjour::Configuration.prepare.specjour / hooks.rb einstelle. Wenn also Rake-Tasks oder anderer Code ausgeführt werden müssen, funktioniert dieser möglicherweise dort.

gerry3
quelle

Antworten:

13

Ich hatte ein ähnliches Problem beim Einrichten des CI-Systems bei der Arbeit, daher habe ich nach und nach ein System entwickelt, um dies zu handhaben. Es ist vielleicht nicht die beste Lösung, aber es funktioniert für mich in meiner Situation und ich bin immer auf der Suche nach besseren Möglichkeiten, Dinge zu tun.

Ich habe eine Testdatenbank, die ich einrichten musste, aber auch gesetzte Daten, die geladen wurden, damit unsere Tests funktionieren.

Die Grundlagen der Fehlerbehebung bei Rake-Aufgaben bestehen darin, Rake mit der Option --trace auszuführen, um zu sehen, was unter der Haube geschieht. Als ich dies tat, stellte ich fest, dass das Ausführen der Rake-Spezifikation eine Reihe von Dingen ausführte, die ich in einer benutzerdefinierten Rake-Aufgabe replizieren (oder nach Belieben ändern) konnte.

Hier ist ein Beispiel dafür, was wir tun.

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

Dies ist nur ein Beispiel und spezifisch für unsere Situation. Sie müssen also herausfinden, was zu tun ist, um Ihr Test-Datenbank-Setup zu erhalten. Mit der Option --trace von Rake ist dies jedoch recht einfach zu bestimmen.

Wenn Sie feststellen, dass das Test-Setup zu lange dauert (wie in unserem Fall), können Sie die Datenbank auch im SQL-Format sichern und von der Testdatenbank direkt in MySQL leiten lassen, um sie zu laden. Auf diese Weise sparen wir einige Minuten beim Einrichten der Testdatenbank. Ich zeige das hier nicht, weil es die Dinge erheblich kompliziert - es muss richtig generiert werden, ohne abgestanden zu werden usw.

HTH

edk750
quelle
Ja, ich habe die Rake-Spezifikation mit --trace ausgeführt und versucht, einige ihrer Aufgaben in meinem Prep-Specjour-Hook zu replizieren, aber es hat noch nicht funktioniert. Ich könnte möglicherweise eine völlig separate Rechenaufgabe schreiben, um die Dinge einzurichten, aber das ist ein weiterer Schritt, den ich vermeiden wollte.
Gerry3
165

Ich würde empfehlen, Ihre Testdatenbank zu löschen, sie dann neu zu erstellen und zu migrieren:

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

Nach diesen Schritten können Sie Ihre Spezifikationen ausführen:

bundle exec rspec spec

gerry3 stellte fest, dass:

Eine einfachere Lösung besteht darin, einfach auszuführen rake db:test:prepare

Wenn Sie jedoch PostgreSQL verwenden, funktioniert dies nicht, da die Rails-Umgebung geladen wird, wodurch eine Datenbankverbindung hergestellt wird. Dies führt dazu, dass der prepareAufruf fehlschlägt, da die Datenbank nicht gelöscht werden kann. Knifflige Sache.

Leviathan
quelle
47
Eine einfachere Lösung besteht darin, einfach auszuführen rake db:test:prepare.
Gerry3
7
Ich habe kein Problem rake db:test:preparemit Postgres. Sie müssen Ihr Problem aus einem anderen Grund sehen.
Gerry3
Wenn db: test: prepare nicht funktioniert, können Sie zumindest die Befehle inline setzen, um einige Eingaben zu sparen:RAILS_ENV=test bundle exec rake db:drop db:create db:schema:load
funwhilelost
11
Sieht aus wie rake db:test:preparein Rails 4 veraltet.
Markquezada
8
Sie können bundle exec rake db:drop db:create db:schema:load RAILS_ENV=test
Rechenaufgaben
14

Die bereitgestellten Lösungen erfordern alle das Laden der Rails-Umgebung, was in den meisten Fällen aufgrund des sehr hohen Overheads und der sehr geringen Geschwindigkeit nicht das gewünschte Verhalten ist. DatabaseCleanergem ist auch ziemlich langsam und fügt Ihrer App eine weitere Abhängigkeit hinzu.

Nach Monaten des Ärgers und Ärgers aus den oben genannten Gründen habe ich endlich herausgefunden, dass die folgende Lösung genau das ist, was ich brauche. Es ist schön, einfach und schnell. In spec_helper.rb:

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

Das Beste daran ist: Es werden nur die Tabellen gelöscht, die Sie effektiv berührt haben (unberührte Modelle werden nicht geladen und erscheinen daher nicht in subclasses, auch der Grund, warum dies vor den Tests nicht funktioniert ). Außerdem wird es nach den Tests ausgeführt, sodass die (hoffentlich) grünen Punkte sofort angezeigt werden.

Der einzige Nachteil dabei ist, dass eine verschmutzte Datenbank vor dem Ausführen von Tests nicht bereinigt wird. Ich bezweifle jedoch, dass dies ein großes Problem ist, da die Testdatenbank normalerweise nicht von externen Tests berührt wird.

Bearbeiten

Da diese Antwort an Popularität gewonnen hat, wollte ich sie der Vollständigkeit halber bearbeiten: Wenn Sie alle Tabellen löschen möchten , auch die nicht berührten, sollten Sie in der Lage sein, so etwas wie die folgenden "Hacks" durchzuführen.

Hack 1 - Vorladen aller Modelle für die subclassesMethode

Bewerten Sie dies, bevor Sie anrufen subclasses:

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

Beachten Sie, dass diese Methode einige Zeit dauern kann!

Hack 2 - manuelles Abschneiden der Tabellen

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

Sie erhalten alle Tabellennamen, mit denen Sie Folgendes tun können:

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
Danyel
quelle
Ordentlich! Das ist sehr nützlich.
Odigity
7

Es scheint, dass in Rails 4.1+ die beste Lösung darin besteht, einfach ActiveRecord::Migration.maintain_test_schema!Ihren rails_helper nachher hinzuzufügen require 'rspec/rails'.

Das heißt, Sie müssen sich keine Sorgen mehr machen, dass Sie die Datenbank vorbereiten müssen.

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

John Morales
quelle
3

In einer frühlingshaften Rails 4-App wird meine bin/setupnormalerweise erweitert, um sie zu enthalten

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

Dies ist sehr ähnlich zu Leviathans Antwort und der Aussaat der Test-DB als

rake db:setup # Erstellen Sie die Datenbank, laden Sie das Schema und initialisieren Sie mit den Startdaten
(verwenden Sie diese Option,
db:reset um auch die Datenbank zuerst zu löschen).

Wie der Kommentar erwähnt, rake db:resettut dies genau das , wenn wir die Datenbank zuerst löschen möchten .

Ich finde auch, dass dies im Vergleich zu mehr Feedback liefert rake db:test:prepare.

Marius Butuc
quelle
0

Ich begann damit, meine Testdatenbank zu löschen rake db:drop RAILS_ENV=test

Beim Versuch, eine neue Testdatenbank zu erstellen, ist ein Problem aufgetreten, da mein Benutzerkonto nicht mit dem Konto identisch war, dem die Datenbanken gehören. Daher habe ich die Datenbank stattdessen in PostgreSQL erstellt.

Geben Sie psqldie Eingabeaufforderung ein und führen Sie die folgenden Schritte aus, um eine Testdatenbank zu erstellen, die ein anderes Konto als Ihr eigenes verwendet. CREATE DATABASE your_database_name OWNER your_db_owner;

Führen Sie dann Ihre Migrationen in der Testumgebung aus. rake db:migrate RAILS_ENV=test

random_user_0891
quelle