Rollback-Transaktion nach @Test

85

Zunächst habe ich auf StackOverflow viele Threads dazu gefunden, aber keiner von ihnen hat mir wirklich geholfen. Es tut mir leid, möglicherweise doppelte Fragen zu stellen.

Ich führe JUnit-Tests mit Spring-Test aus. Mein Code sieht so aus

@RunWith(SpringJUnit4ClassRunner.class)  
@ContextConfiguration(locations = {})
public class StudentSystemTest {

    @Autowired
    private StudentSystem studentSystem;

    @Before
    public void initTest() {
    // set up the database, create basic structure for testing
    }

    @Test
    public void test1() {
    }    
    ...  
}

Mein Problem ist, dass meine Tests andere Tests NICHT beeinflussen sollen. Ich möchte also für jeden Test so etwas wie ein Rollback erstellen. Ich habe viel danach gesucht, aber bisher nichts gefunden. Ich benutze dafür Hibernate und MySql

Jan Vorcak
quelle
Was meinst du mit Rollback? Datenbank bereinigen?
Gaurav
5
Setzen Sie es auf genau den gleichen Zustand wie nach der AusführunginitTest
Jan Vorcak

Antworten:

127

Fügen Sie einfach eine @TransactionalAnmerkung zu Ihrem Test hinzu:

@RunWith(SpringJUnit4ClassRunner.class)  
@ContextConfiguration(locations = {"testContext.xml"})
@Transactional
public class StudentSystemTest {

Standardmäßig startet Spring eine neue Transaktion rund um Ihre Testmethode und @Before/ oder @AfterRückrufe, die am Ende zurückgesetzt wird. Es funktioniert standardmäßig, es reicht aus, einen Transaktionsmanager im Kontext zu haben.

Von: 10.3.5.4 Transaktionsmanagement (fett gedruckt ):

Im TestContext-Framework werden Transaktionen vom TransactionalTestExecutionListener verwaltet. Beachten Sie, dass TransactionalTestExecutionListenerist standardmäßig so konfiguriert , auch wenn Sie nicht explizit erklären @TestExecutionListenersauf Ihrer Testklasse. Um die Unterstützung für Transaktionen zu aktivieren, müssen Sie jedoch eine PlatformTransactionManagerBean in dem von der @ContextConfigurationSemantik geladenen Anwendungskontext bereitstellen . Darüber hinaus müssen Sie @Transactionalfür Ihre Tests entweder auf Klassen- oder Methodenebene deklarieren .

Tomasz Nurkiewicz
quelle
2
Nun, ich habe es schon einmal versucht und es funktioniert immer noch nicht, vielleicht ... kann das Problem sein, dass ich den PlatformTransactionManager nicht definiert habe, wie kann ich das tun?
Jan Vorcak
@javo: Wie änderst du die Datenbank? Wenn Sie Jpa / Hibernate / JdbcTemplate / ... verwenden, müssen einige vorhanden sein PlatformTransactionManager. Wie wird Spring sonst von Ihren Transaktionen und Ihrer Datenbank erfahren?
Tomasz Nurkiewicz
1
Der Link in dieser Antwort ist nicht mehr korrekt. siehe user2418306 Antwort unten für die richtige Verbindung und mehr Kontext aus der Frühjahr - Dokumentation.
DaveyDaveDave
1
Ich arbeite nicht, jede Methode erfordert eine separate Transaktion, kann es nicht für die ganze Klasse tun
Kamil Nekanowicz
Ich teste eine Einfügung in eine Tabelle. Bei @Transactional wird der Befehl insert nicht einmal für die Datenbank ausgegeben (ich kann das sehen, weil die Konsole den Ruhezustand show-sql true hat). Funktioniert in vielen Fällen einwandfrei. Bei einem meiner Tests wird jedoch überprüft, ob die Datenbank aufgrund einer Datenbankeinschränkung eine DataAccessException ausgibt. In diesem Fall schlägt der Test fehl, da der Rollback "im Speicher" erfolgt und die Datenbank niemals aufgerufen wird. Lösung: Verwenden Sie @Transactionalauf @TestMethodenebene und nicht auf Klassenebene.
Paulo Merson
17

Nebenbei : Der Versuch, die Antwort von Tomasz Nurkiewicz zu ändern, wurde abgelehnt:

Diese Bearbeitung macht den Beitrag nicht einmal ein bisschen leichter zu lesen, leichter zu finden, genauer oder zugänglicher. Änderungen sind entweder völlig überflüssig oder beeinträchtigen die Lesbarkeit aktiv.


Korrekter und dauerhafter Link zum entsprechenden Dokumentationsabschnitt über Integrationstests.

Um die Unterstützung für Transaktionen zu aktivieren, müssen Sie eine PlatformTransactionManagerBean in der konfigurieren ApplicationContext, die über die @ContextConfigurationSemantik geladen wird .

@Aufbau
@PropertySource ("application.properties")
öffentliche Klasse Persistenz {
    @Autowired
    Umwelt env;

    @Bohne
    DataSource dataSource () {
        neue DriverManagerDataSource zurückgeben (
                env.getProperty ("datasource.url"),
                env.getProperty ("datasource.user"),
                env.getProperty ("datasource.password")
        );
    }}

    @Bohne
    PlatformTransactionManager transactionManager () {
        neuen DataSourceTransactionManager (dataSource ()) zurückgeben;
    }}
}}

Darüber hinaus müssen Sie die @TransactionalAnnotation von Spring für Ihre Tests entweder auf Klassen- oder Methodenebene deklarieren .

@RunWith (SpringJUnit4ClassRunner.class)
@ContextConfiguration (classes = {Persistence.class, SomeRepository.class})
@Transactional
öffentliche Klasse SomeRepositoryTest {...}

Durch das Kommentieren einer Testmethode mit @Transactionalwird der Test innerhalb einer Transaktion ausgeführt, die nach Abschluss des Tests standardmäßig automatisch zurückgesetzt wird. Wenn eine Testklasse mit Anmerkungen versehen ist @Transactional, wird jede Testmethode innerhalb dieser Klassenhierarchie innerhalb einer Transaktion ausgeführt.

user2418306
quelle
12

Die Antworten, in denen das Hinzufügen erwähnt wird, @Transactionalsind korrekt, aber der Einfachheit halber könnten Sie einfach Ihre Testklasse haben extends AbstractTransactionalJUnit4SpringContextTests.

matt b
quelle
1
Das Hinzufügen der Annotation '@Transactional' auf Klassenebene funktioniert nicht. Das Hinzufügen der Annotation '@Transactional' für jede Funktion funktioniert separat und erweitert AbstractTransactionalJUnit4SpringContextTests funktioniert auch
Kamil Nekanowicz
5

Ich weiß, ich bin zu spät, um eine Antwort zu posten, aber ich hoffe, dass es jemandem helfen könnte. Außerdem habe ich gerade dieses Problem gelöst, das ich mit meinen Tests hatte. Folgendes hatte ich in meinem Test:

Meine Testklasse

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "path-to-context" })
@Transactional
public class MyIntegrationTest 

Kontext xml

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
   <property name="driverClassName" value="${jdbc.driverClassName}" />
   <property name="url" value="${jdbc.url}" />
   <property name="username" value="${jdbc.username}" />
   <property name="password" value="${jdbc.password}" />
</bean>

Ich hatte immer noch das Problem, dass die Datenbank nicht automatisch bereinigt wurde.

Das Problem wurde behoben, als ich BasicDataSource die folgende Eigenschaft hinzufügte

<property name="defaultAutoCommit" value="false" />

Ich hoffe es hilft.

Atul Kumbhar
quelle
Nun, also legen Sie Ihre Aussagen manuell fest? Sind Sie sicher, dass Ihre Daten überhaupt in Ihre Datenbank geschrieben wurden?
Franta Kocourek
Wenn Sie Schwierigkeiten haben, Spring Transactions zu verstehen, stellen Sie sicher, dass Ihre Datenquelle NICHT auf automatische Festschreibung eingestellt ist. Andernfalls werden Sie verrückt, wenn Sie herausfinden möchten, warum @Transactional anscheinend nichts bewirkt.
Joe Ernst
2

Sie müssen Ihren Test mit einem Spring-Kontext und einem Transaktionsmanager ausführen, z.

@RunWith(SpringJUnit4ClassRunner.class)  
@ContextConfiguration(locations = {"/your-applicationContext.xml"})
@TransactionConfiguration(transactionManager="txMgr")
public class StudentSystemTest {

     @Test
     public void testTransactionalService() {
         // test transactional service
     }

     @Test
     @Transactional
     public void testNonTransactionalService() {
         // test non-transactional service
     }
}

3.5.8. Transaction ManagementWeitere Einzelheiten finden Sie im Kapitel der Federreferenz.

Johan Sjöberg
quelle
0

Zusätzlich zur Add- @TransactionalOn- @TestMethode müssen Sie auch hinzufügen@Rollback(false)

user2615724
quelle
-4

Sie können den Rollback deaktivieren:

@TransactionConfiguration(defaultRollback = false)

Beispiel:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@Transactional
@TransactionConfiguration(defaultRollback = false)
public class Test {
    @PersistenceContext
    private EntityManager em;

    @org.junit.Test
    public void menge() {
        PersistentObject object = new PersistentObject();
        em.persist(object);
        em.flush();
    }
}
DwD
quelle
6
Das ist genau das Gegenteil, was das OP verlangt
Adam Michalik
Dies sollte ein Kommentar zur akzeptierten Antwort gewesen sein.
DerMike