在cucumber-jvm测试中回滚事务

时间:2014-09-27 18:41:16

标签: java spring hibernate transactions cucumber-jvm

我目前正在使用Spring开发REST Web服务,使用Hibernate作为ORM并使用黄瓜编写验收测试。

为了能够在场景之间回滚事务,我有以下代码在每个场景之前创建一个事务并在之后回滚。

package com.orange.cainet.cucumber;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import cucumber.api.java.After;
import cucumber.api.java.Before;

@WebAppConfiguration
@ContextConfiguration("classpath:applicationContext.xml")
public class RollbackTransactionsBetweenScenarios {

    @Autowired
    PlatformTransactionManager transactionManager;

    TransactionStatus transaction;

    @Before
    public void beforeScenario(){       
        transaction = transactionManager.getTransaction(new DefaultTransactionDefinition());
    }

    @After
    public void afterScenario(){
        transactionManager.rollback(transaction);
    }
}

在步骤定义类中,我使用我的实体的自动装配的CrudRepository来创建Given语句中的内容并断言then语句中的内容。

我使用MockMvc来模拟应用程序上下文并使用(post,get,...)发送虚假请求,并将ResultActions保存为字段以在断言中使用它。

直到最近这对我有用,问题在使用@Column(unique = true)时开始,以防止实体中某个字段的重复,并使用DataIntegrityViolationException来知道我将要实体的实体是什么时候保存有重复的字段

try{
    newUser = userRespoitory.save(newUser);         
}
catch(DataIntegrityViolationException exception){
    throw new UserNameAlreadyExistsException();
}  

问题是抛出DataIntegrityViolationException后,每当我在任何黄瓜步骤定义函数中使用UserRepository.findOne()时,测试都会抛出错误

org.hibernate.AssertionFailure: null id in com.komalo.domain.User entry (don't flush the Session after an exception occurs)

我理解这是因为我在每个场景之前创建的事务,如果我在使用UserRepository之前回滚此事务,它会正常工作。

所以我的问题是:

1)这是写测试的正确方法吗?因为我使用的是UserReposistory,这是我正在进行验收测试的一部分,但我认为,因为它是由Spring Data实现的,所以没关系。

2)即使抛出异常,还有办法继续使用交易吗?

3)这可以通过不依赖于DataIntegrityViolationException来轻松解决,而是通过在UserRepository.findOneByUsername()之类的接口中创建一个额外的方法来手动检查并使用它,但这不是一个额外的select语句正在执行吗? ?

2 个答案:

答案 0 :(得分:1)

好吧,我没有明确的答案,但您可以更好地清理并重新插入数据,或者为代码添加一些逻辑,如:

if(userRepositry.findOne(newUser.getName())==null)
newUser = userRespoitory.save(newUser);         

但我不认为这是使用BDD的正确方法。

您需要将数据创建与存储库和生产代码状态分开,同时您的测试必须彼此独立,因此clean->run对我来说是一个很好的策略。

答案 1 :(得分:0)

这是一个特殊的@txn(特定于Java)标记,您可以在功能或场景(大纲)上添加...我认为这是正确的方法。

但是......对我来说,它在运行特定场景,场景大纲甚至整个功能文件时都有效。出于某种原因,当我通过带有@RunWith(Cucumber.class)注释的Java测试类运行这些功能时,它似乎被忽略了。

关于您的子问题#2:另一个特殊标记为@allow-rescue

  

关闭Cucumber对标记场景的异常捕获。   当正在测试的代码需要引发和处理时使用   异常。