Transação de rollback após @Test

86

Em primeiro lugar, encontrei muitos tópicos no StackOverflow sobre isso, mas nenhum deles realmente me ajudou, então desculpe por fazer uma pergunta possivelmente duplicada.

Estou executando testes JUnit usando spring-test, meu código se parece com este

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

Meu problema é que eu quero que meus testes NÃO influenciem outros testes. Então, eu gostaria de criar algo como rollback para cada teste. Procurei muito por isso, mas não encontrei nada até agora. Estou usando Hibernate e MySql para isso

Jan Vorcak
fonte
O que você quer dizer com reversão? Limpando o banco de dados?
Gaurav
5
definindo exatamente o mesmo estado em que estava após a execuçãoinitTest
Jan Vorcak

Respostas:

129

Basta adicionar uma @Transactionalanotação no topo do seu teste:

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

Por padrão, o Spring irá iniciar uma nova transação envolvendo seu método de teste e @Before/ @Aftercallbacks, revertendo no final. Funciona por padrão, basta ter algum gerenciador de transações no contexto.

De: 10.3.5.4 Gestão de transações (negrito):

Na estrutura TestContext, as transações são gerenciadas pelo TransactionalTestExecutionListener. Observe que TransactionalTestExecutionListeneré configurado por padrão , mesmo se você não declarar explicitamente @TestExecutionListenersem sua classe de teste. Para ativar o suporte para transações, no entanto, você deve fornecer um PlatformTransactionManagerbean no contexto do aplicativo carregado pela @ContextConfigurationsemântica. Além disso, você deve declarar @Transactionalno nível da classe ou do método para seus testes .

Tomasz Nurkiewicz
fonte
2
bem, já tentei isso antes, e ainda não funciona, talvez ... o problema é que não defini PlatformTransactionManager, como posso fazer isso?
Jan Vorcak
@javo: como você está modificando o banco de dados? Se você estiver usando Jpa / Hibernate / JdbcTemplate / ... deve haver algum PlatformTransactionManager. Caso contrário, como o Spring saberá sobre suas transações e banco de dados?
Tomasz Nurkiewicz
1
O link desta resposta não está mais correto; consulte a resposta do usuário 2418306 abaixo para obter o link correto e mais contexto da documentação do Spring.
DaveyDaveDave
1
Eu não funciona, cada método requer uma transação separada, não posso fazer isso para a classe inteira
Kamil Nekanowicz
Estou testando uma inserção em uma mesa. Com @Transactional, o comando insert nem mesmo é emitido para o banco de dados (posso ver isso porque o console tem hibernate show-sql true). Funciona bem em muitos casos. Mas um dos meus testes verifica se o banco de dados emite uma DataAccessException devido a uma restrição do banco de dados. Nesse caso, o teste falha porque o rollback acontece "na memória" e o banco de dados nunca é chamado. Solução: use @Transactionalno @Testnível do método e não no nível da classe.
Paulo Merson
17

À parte: a tentativa de alterar a resposta de Tomasz Nurkiewicz foi rejeitada:

Esta edição não torna a postagem nem um pouco mais fácil de ler, encontrar, mais precisa ou mais acessível. As alterações são completamente supérfluas ou prejudicam ativamente a legibilidade.


Link correto e permanente para a seção relevante da documentação sobre o teste de integração.

Para habilitar o suporte para transações, você deve configurar um PlatformTransactionManagerbean no ApplicationContextque é carregado via @ContextConfigurationsemântica.

@Configuração
@PropertySource ("application.properties")
public class Persistence {
    @Autowired
    Env do ambiente;

    @Feijão
    DataSource dataSource () {
        retornar novo DriverManagerDataSource (
                env.getProperty ("datasource.url"),
                env.getProperty ("datasource.user"),
                env.getProperty ("datasource.password")
        );
    }

    @Feijão
    PlatformTransactionManager transactionManager () {
        retornar novo DataSourceTransactionManager (dataSource ());
    }
}

Além disso, você deve declarar a @Transactionalanotação do Spring no nível da classe ou do método para seus testes.

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

Anotar um método de teste com @Transactionalfaz com que o teste seja executado em uma transação que, por padrão, será revertida automaticamente após a conclusão do teste. Se uma classe de teste for anotada com @Transactional, cada método de teste dentro dessa hierarquia de classe será executado em uma transação.

user2418306
fonte
12

As respostas que mencionam adição @Transactionalestão corretas, mas para simplificar, você pode apenas ter sua aula de teste extends AbstractTransactionalJUnit4SpringContextTests.

matt b
fonte
1
adicionar a anotação '@Transactional' no nível da classe não funciona, adicionar a anotação '@Transactional' separadamente para cada função funciona e estende a função AbstractTransactionalJUnit4SpringContextTests
Kamil Nekanowicz
5

Eu sei, estou muito tarde para postar uma resposta, mas espero que possa ajudar alguém. Além disso, acabei de resolver esse problema que tive com meus testes. Isso é o que eu tive em meu teste:

Minha aula de teste

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

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

Eu ainda tinha o problema de que o banco de dados não estava sendo limpo automaticamente.

O problema foi resolvido quando adicionei a seguinte propriedade a BasicDataSource

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

Espero que ajude.

Atul Kumbhar
fonte
Bem, então você confirma suas declarações manualmente? Tem certeza de que seus dados foram gravados em seu banco de dados?
franta kocourek de
Para qualquer um que esteja lutando para entender as transações do Spring, certifique-se de que sua fonte de dados NÃO esteja configurada para auto-commit ou você vai enlouquecer tentando descobrir por que @Transactional parece não fazer nada.
Joe Ernst de
2

Você precisa executar seu teste com um contexto Spring e um gerenciador de transações, por exemplo,

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

Consulte o capítulo 3.5.8. Transaction Managementde referência do Spring para obter mais detalhes.

Johan Sjöberg
fonte
0

Além de adicionar @Transactionalno @Testmétodo, você também precisa adicionar@Rollback(false)

user2615724
fonte
-5

Você pode desativar o Rollback:

@TransactionConfiguration(defaultRollback = false)

Exemplo:

@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
fonte
6
Isso é exatamente o oposto do que o OP está pedindo
Adam Michalik
Isso deveria ter sido um comentário para a resposta aceita.
DerMike