Ich habe versucht, @TransactionalEvents
zu testen (eine Funktion von Spring 4.2 https://spring.io/blog/2015/02/11/better-application-events-in-spring-framework-4-2) mit unseren bestehenden Feder JUnit-Tests (lief entweder über @TransactionalTestExecutionListener
oder Subklassifizieren AbstractTransactionalUnit4SpringContextTests
aber, so scheint es, als gäbe es eine erzwungene Wahl ist - - entweder den Test ohne @Rollback
Anmerkung ausführen oder die Ereignisse nicht ausgelöst hat über eine gute Art und Weise jemand kommen @TransactionalEvents zu testen, während der Lage, Tests @RollbackTesting @TransactionalEvents und @Rollback
Antwort
Stéphane Nicoll ist richtig: wenn die TransactionPhase
für.? Ihr @TransactionalEventListener
ist auf AFTER_COMMIT
gesetzt, dann macht ein Transaktionstest mit automatischer Rollback-Semantik keinen Sinn, weil das Ereignis nie kommen wird gefeuert.
Mit anderen Worten, es gibt keine Möglichkeit, ein Ereignis ausgelöst zu haben, nachdem eine Transaktion festgeschrieben wurde, wenn diese Transaktion niemals ausgeführt wird.
Wenn Sie also wirklich möchten, dass das Ereignis ausgelöst wird, müssen Sie die Transaktion festschreiben lassen (z. B. indem Sie Ihre Testmethode mit @Commit
annotieren). Um nach dem Festschreiben aufzuräumen, sollten Sie @Sql
in isolierten Modus verwenden können, um Bereinigungsskripts nach auszuführen, die die Transaktion festgeschrieben hat. Zum Beispiel so etwas wie die folgenden (ungetestet Code) könnte für Sie arbeiten:
@Transactional
@Commit
@Sql(scripts = "/cleanup.sql", executionPhase = AFTER_TEST_METHOD,
config = @SqlConfig(transactionMode = TransactionMode.ISOLATED))
@Test
public void test() { /* ... */ }
Grüße,
Sam (Autor des Frühlings Testcontext Rahmen)
Ok, danke. Während ich daran gearbeitet habe, ist ein Teil meines Missverständnisses, dass verschachtelte \ @Transactional-Aufrufe unabhängig von der Weiterleitung das Ereignis immer noch halten, bis die höchste \ @Transactional-Methode abgeschlossen ist. – adam
Sam Brannen-Lösung funktioniert fast mit in Bezug auf adams Kommentar.
Tatsächlich werden die mit @TransactionalEventListener
annotierten Methoden aufgerufen, nachdem die Testmethodentransaktion festgeschrieben wurde. Dies ist so, weil die aufrufende Methode, die das Ereignis auslöst, innerhalb einer logischen Transaktion, nicht einer physischen ausgeführt wird.
Wenn die aufrufende Methode innerhalb einer neuen physischen Transaktion ausgeführt wird, werden die mit @TransactionalEventListener
annotierten Methoden stattdessen zum richtigen Zeitpunkt aufgerufen, d. H. Bevor die Testmethodentransaktion festgeschrieben wird.
Auch brauchen wir nicht @Commit
auf die Testmethoden, da uns diese Transaktionen eigentlich egal sind. Allerdings benötigen wir die Anweisung @Sql(...)
, wie von Sam Brannen erklärt, die festgeschriebenen Änderungen der aufrufenden Methode rückgängig zu machen.
Siehe das kleine Beispiel unten.
Zuerst wird der Zuhörer, die aufgerufen wird, wenn die Transaktion (Standardverhalten von @TransactionalEventListener
) begangen wird:
@Component
public class MyListener {
@TransactionalEventListener
public void when(MyEvent event) {
...
}
}
Dann wird die Anwendung Dienst, der das Ereignis durch die obige Klasse angehört veröffentlicht.Beachten Sie, dass die Transaktionen sind so konfiguriert, um neue physische jedesmal, wenn eine Methode aufgerufen wird (siehe Spring Framework doc für weitere Details):
@Service
@Transactional(propagation = Propagation.REQUIRES_NEW)
public class MyApplicationService {
public void doSomething() {
// ...
// publishes an instance of MyEvent
// ...
}
}
Schließlich wird das Testverfahren, wie Sam Brannen vorgeschlagen, aber ohne die @Commit
Anmerkung, die nicht ist benötigt an dieser Stelle:
@Transactional
@Sql(scripts = "/cleanup.sql", executionPhase = AFTER_TEST_METHOD,
config = @SqlConfig(transactionMode = TransactionMode.ISOLATED))
@Test
public void test() {
MyApplicationService target = // ...
target.doSomething();
// the event is now received by MyListener
// assertions on the side effects of MyListener
// ...
}
diese Weise ist es wie ein Zauber funktioniert :-)
Wenn Sie die Transaktion Rollback, die den Test verwendet, und Sie wollen sicherstellen, dass ein bestimmtes Stück Code machen auf commit aufgerufen werden , du sollst Ich würde das anders testen. Ich würde argumentieren, dass Sie vielleicht das Framework testen und nicht Ihren eigenen Code. –
Nicht sicher, stimme ich zu, aber ich werde darüber nachdenken. Mit @Rollback wird die Datenbank zurückgesetzt, um den ursprünglichen Teststatus wiederherzustellen (um sicherzustellen, dass die Wahrscheinlichkeit einer Interaktion zwischen Tests geringer ist). – adam
Ich weiß was es macht. Das Problem besteht darin, dass Sie ein Ereignis anfordern, das durch die Transaktion ausgeführt wurde. Es hat nicht. –