2013-08-09 6 views
7

In unserer Rails 4.0-Anwendung mit MySql verwenden wir rspec zusammen mit dem Datenbank-cleaner konfiguriert mit Strategie: Transaktion, um unsere Datenbank für jeden Testfall zu bereinigen. Wenn wir benutzerdefinierte Transaktionen haben, die zurückgesetzt werden sollen, funktioniert es nicht.benutzerdefinierte Transaktion funktioniert nicht mit database_cleaner in rspec

Ohne database_cleaner Juwel und nur mit dem Standard-Weg:

config.use_transactional_fixtures = true 

alles funktioniert wie aspected. Für das Ausführen von Feature-Tests mit JavaScript benötigen wir jedoch database_cleaner, um die Fixture Deletion-Strategie zu ändern: truncation.

Wie können wir database_cleaner zusammen mit benutzerdefinierten Transaktionen verwenden und warum unterscheidet es sich von der Standard-rspec-Transaktionsstrategie?

+0

Ich bin sicher, dass Sie wissen: DatabaseCleaner.strategy =: Löschen/DatabaseCleaner.strategy =: Abschneiden, wie Sie es selbst erwähnen. Ich bin immer noch interessiert, wenn dies ein Problem innerhalb von database_cleaner ist: Transaktionsstrategie, oder wenn dies nur ein allgemeines Problem mit geschachtelten Transaktionen ist manchmal unerwartet verhält. –

Antwort

11

Das Problem ist, dass database_cleaner ActiveRecord.rollback am Ende eines Tests aufruft - die Sie auch in Ihrem Code verwenden. InnoDB/MySQL unterstützt keine echten verschachtelten Transaktionen, sodass geschachtelte Transaktionen im Code nicht wirklich als Transaktionen behandelt werden, es sei denn, sie werden explizit dazu aufgerufen.

Betrachten Sie diesen Block (von dem Active docs):

User.transaction do 
    User.create(username: 'Kotori') 
    User.transaction do 
    User.create(username: 'Nemu') 
    raise ActiveRecord::Rollback 
    end 
end 

Was tun Sie nach dem Transfer abbrechen Anruf passieren erwarten? Sie erwarten, dass es den Nemu-Benutzer zurücksetzt und Sie bei Kotori belässt, richtig? Nun, was tatsächlich passiert ist, dass Kotori und Nemu beide erschaffen wurden. Der Rollback wird nicht ausgelöst, weil Sie sich in einer geschachtelten Transaktion befinden (und AR kümmert sich nur momentan um den übergeordneten), und die übergeordnete Transaktion (die eine tatsächliche Datenbanktransaktion hat), sieht den Rollback-Aufruf nicht - so wie er aufgerufen wird ein isolierter Block. Es ist komisch.

Lösung:

Klass.transaction(requires_new: true) 

Wenn Sie requires_new gesetzt ist, wird Active verwenden oder eine verschachtelte Transaktion-Pseudo verwenden (für Postgres es wird verschachtelte Transaktionen machen, für MySQL/InnoDB wird es Speicherpunkte machen). Wenn Sie ein Rollback mit ActiveRecord aufrufen, ermittelt es seinen Umfang und gibt das ordnungsgemäße Rollback aus.

Die andere Lösung ist, Trunkierung oder Löschung als eine Strategie für Ihre Tests, die Transaktionen beinhalten, zu verwenden.

+0

großartige Erklärung. Vielen Dank! –