Transazione di rollback dopo @Test


86

Prima di tutto, ho trovato molti thread su StackOverflow a riguardo, ma nessuno di loro mi ha davvero aiutato, quindi mi dispiace porre una domanda forse duplicata.

Sto eseguendo i test JUnit usando spring-test, il mio codice ha questo aspetto

@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() {
    }    
    ...  
}

Il mio problema è che voglio che i miei test NON influenzino altri test. Quindi vorrei creare qualcosa come il rollback per ogni test. Ho cercato molto per questo, ma finora non ho trovato nulla. Sto usando Hibernate e MySql per questo


Cosa intendi per rollback? Pulire il database?
Gaurav,

5
impostandolo esattamente nello stesso stato in cui era dopo l'esecuzioneinitTest
Jan Vorcak,

Risposte:


129

Basta aggiungere @Transactionalun'annotazione sopra il test:

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

Per impostazione predefinita, Spring inizierà una nuova transazione che circonda il tuo metodo di test e @Before/ @Aftercallback, ripristinando alla fine. Funziona di default, è sufficiente avere un gestore di transazioni nel contesto.

Da: 10.3.5.4 Gestione delle transazioni (in grassetto):

Nel framework TestContext, le transazioni sono gestite da TransactionalTestExecutionListener. Nota che TransactionalTestExecutionListenerè configurato per impostazione predefinita , anche se non dichiari esplicitamente @TestExecutionListenersnella tua classe di test. Per abilitare il supporto per le transazioni, tuttavia, è necessario fornire un PlatformTransactionManagerbean nel contesto dell'applicazione caricato dalla @ContextConfigurationsemantica. Inoltre, è necessario dichiarare @Transactionala livello di classe o metodo per i test .


2
beh, l'ho provato prima, e ancora non funziona, forse ... il problema può essere che non ho definito PlatformTransactionManager, come posso farlo?
Jan Vorcak,

@javo: come stai modificando il database? Se stai usando Jpa / Hibernate / JdbcTemplate / ... ce ne devono essere alcuni PlatformTransactionManager. Altrimenti come farà Spring a conoscere le tue transazioni e il tuo database?
Tomasz Nurkiewicz,

1
Il collegamento in questa risposta non è più corretto; vedere la risposta di user2418306 di seguito per il collegamento corretto e più contesto dalla documentazione di Spring.
DaveyDaveDave

1
Non funziona, ogni metodo richiede una transazione separata, non posso farlo per l'intera classe
Kamil Nekanowicz

Sto testando un inserto in una tabella. Con @Transactional, il comando di inserimento non viene nemmeno emesso contro il database (posso vederlo perché la console ha l'ibernazione show-sql true). Funziona bene in molti casi. Ma uno dei miei test controlla che il database emetta un'eccezione DataAccessException a causa di un vincolo del database. In questo caso il test fallisce perché il rollback avviene "in memoria" e il database non viene mai chiamato. Soluzione: utilizzare @Transactionala @Testlivello di metodo e non a livello di classe.
Paulo Merson

17

A parte: il tentativo di modificare la risposta di Tomasz Nurkiewicz è stato respinto:

Questa modifica non rende il post nemmeno un po 'più facile da leggere, più facile da trovare, più accurato o più accessibile. Le modifiche sono completamente superflue o danneggiano attivamente la leggibilità.


Collegamento corretto e permanente alla sezione pertinente della documentazione sui test di integrazione.

Per abilitare il supporto per le transazioni, è necessario configurare un PlatformTransactionManagerbean in ApplicationContextche viene caricato tramite @ContextConfigurationsemantica.

@Configurazione
@PropertySource ("application.properties")
public class Persistence {
    @Autowired
    Ambiente env;

    @Fagiolo
    DataSource dataSource () {
        restituire nuovo DriverManagerDataSource (
                env.getProperty ("datasource.url"),
                env.getProperty ("datasource.user"),
                env.getProperty ("datasource.password")
        );
    }

    @Fagiolo
    PlatformTransactionManager transactionManager () {
        restituire nuovo DataSourceTransactionManager (dataSource ());
    }
}

Inoltre, devi dichiarare l' @Transactionalannotazione di Spring a livello di classe o metodo per i tuoi test.

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

L'annotazione di un metodo di test con @Transactionalcausa l'esecuzione del test all'interno di una transazione che, per impostazione predefinita, verrà automaticamente annullata dopo il completamento del test. Se una classe di test viene annotata con @Transactional, ogni metodo di test all'interno di quella gerarchia di classi verrà eseguito all'interno di una transazione.


12

Le risposte che menzionano l'aggiunta @Transactionalsono corrette, ma per semplicità potresti semplicemente avere la tua lezione di prova extends AbstractTransactionalJUnit4SpringContextTests.


1
l'aggiunta dell'annotazione "@Transactional" a livello di classe non funziona, l'aggiunta dell'annotazione "@Transactional" separatamente per ogni funzione funziona ed estende anche AbstractTransactionalJUnit4SpringContextTests
Kamil Nekanowicz

5

Lo so, sono troppo tardi per pubblicare una risposta, ma spero che possa aiutare qualcuno. Inoltre, ho appena risolto questo problema che avevo con i miei test. Questo è quello che ho avuto nel mio test:

La mia lezione di prova

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

Contesto 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>

Avevo ancora il problema che il database non veniva pulito automaticamente.

Il problema è stato risolto quando ho aggiunto la seguente proprietà a BasicDataSource

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

Spero che sia d'aiuto.


Bene, quindi invii le tue dichiarazioni manualmente? Sei sicuro che i tuoi dati siano stati scritti nel tuo database?
franta kocourek

Per chiunque abbia difficoltà a comprendere le transazioni di primavera, assicurati che l'origine dati NON sia impostata per il commit automatico o diventerai matto cercando di capire perché @Transactional sembra non fare nulla.
Joe Ernst

2

È necessario eseguire il test con un contesto Spring e un gestore delle transazioni, ad es.

@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
     }
}

Vedere il capitolo 3.5.8. Transaction Managementdel riferimento Primavera per ulteriori dettagli.


0

Oltre ad aggiungere @Transactionalil @Testmetodo, devi anche aggiungere@Rollback(false)


-5

Puoi disabilitare il rollback:

@TransactionConfiguration(defaultRollback = false)

Esempio:

@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();
    }
}

6
Questo è esattamente l'opposto di ciò che chiede l'OP
Adam Michalik

Questo avrebbe dovuto essere un commento alla risposta accettata.
DerMike
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.