5

我不明白为什么这个集成测试失败。我可以,或者通过在集成测试Grails集成测试和交易

我意识到,集成测试本身的事务运行设置transactional = false得到测试通过删除@Transactional(propagation = Propagation.REQUIRES_NEW)注释上面的服务方法来传递,这就是为什么我有服务方法的注释。

class DbTests extends GrailsUnitTestCase { 

boolean transactional = true 
def customerService 

void testTransactionsCommit() { 
    def orderIds = [1, 2, 3] 
    orderIds.each { // lets make sure they all start out as Active 
     def order = Order.get(it) 
     order.isActive = true 
     order.save(flush:true, validate:true, failOnError: true) 
    } 

    customerService.cancelOrders(orderIds) 

    orderIds.each { 
     def order = Order.get(it).refresh() 
     assertEquals false, order.isActive 
    } 
} 

和我的服务方法的定义:

class CustomerService { 

boolean transactional = true 
@Transactional(propagation = Propagation.REQUIRES_NEW) 
def cancelOrders(def orderIds) { 
    orderIds.each { 
     Order order = Order.get(it) 
     if(order.id == 5) // 
      throw new RuntimeException('Simulating an exception here, panic!') 
     order.isActive = false 
     order.save(flush:true, validate:true, failOnError: true) 
     println "Order.id = $order.id is ${order.isActive? 'ACTIVE' : 'CANCELLED'}" 
    } 
}} 

Order实体是一个简单的域对象,我对Grails的1.2.1,MySQL的5.x的(方言= org.hibernate作为。 dialect.MySQL5InnoDBDialect)

我已经看到了这个相关的职位,但仍没有雪茄:(

Grails Service Transactions

回答

8

嵌套的内部事务已经提交的数据更改实际上应该在父事务中立即可见。

我真的不知道他们为什么是而不是GroovyTestCase的事务上下文中。 Others don't know, as well, and are using similar approaches to mine

考虑下面的测试用例。测试用例本身是而不是事务性,但调用事务性方法。 - 这符合预期。

class TransactionalMethodTest extends GroovyTestCase { 
    static transactional = false // test case is not transactional 
    def customerService 

    void testTransactionsCommit() { 
     // start a new transaction, 
     // setting order 1 inactive 
     setOrderInactive() 
     assert ! Order.get(1).isActive 
    } 

    @Transactional(propagation = Propagation.REQUIRED) 
    private void setOrderInactive() { 
     // make sure that order 1 is active 
     Order order = Order.get(1) 
     order.isActive = true 
     order.save(flush:true) 

     assert Order.get(1).isActive 

     // the following method acts in isolation level 
     // Propagation.REQUIRES_NEW, which means, 
     // a new, nested, transaction is started 
     // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
     customerService.cancelOrders([1]) 

     // changes from the nested transaction are 
     // visible, instantly 
     assert ! Order.get(1).isActive 
     // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 
    } 
} 

现在考虑以下“正常”事务性测试用例。嵌套事务中的数据更改是而不是在父事务中可见。

我只能说,事务测试用例不适用于嵌套事务,所以使用了高于的非事务测试用例。
如果我们不明白原因,我们至少可以知道我们的选择。

class TransactionalTestCaseTests extends GroovyTestCase { 
    static transactional = true // default; Propagation.REQUIRED 
    def customerService 

    void testTransactionsCommit() { 
     // make sure that order 1 is active 
     Order order = Order.get(1) 
     order.isActive = true 
     order.save(flush:true) 

     assert Order.get(1).isActive 

     // the following method acts in isolation level 
     // Propagation.REQUIRES_NEW, which means, 
     // a new, nested, transaction is started 
     // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
     customerService.cancelOrders([1]) 

     // the changes from the inner transaction 
     // are not yet visible 
     assert Order.get(1).isActive 
     // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 
    } 

    @Override 
    protected void tearDown() throws Exception { 
     // the changes from the inner transaction 
     // are still not visible 
     assert Order.get(1).isActive 

     super.tearDown(); 
    } 
} 

不相关的主要问题,但你的整体意图,这里是检查嵌套事务是否被回滚,正确的测试案例:

class NestedTransactionRolledBackTests extends GroovyTestCase { 
    static transactional = false // test case is not transactional 
    def customerService 

    void testTransactionsCommit() { 
     // start a new transaction, 
     // setting order 1 active 
     setOrderActive() 
     assert Order.get(1).isActive 
    } 

    @Transactional(propagation = Propagation.REQUIRED) 
    private void setOrderActive() { 
     // make sure that order 1 is active 
     Order order = Order.get(1) 
     order.isActive = true 
     order.save(flush:true) 

     assert Order.get(1).isActive 

     // the following method acts in isolation level 
     // Propagation.REQUIRES_NEW, which means, 
     // a new, nested, transaction is started. 
     // This transaction will fail, and be rolled back. 
     // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
     shouldFail(NullPointerException) { 
      customerService.cancelOrders([1, -999]) 
     } 

     // changes from the nested transaction are 
     // visible, instantly. 
      // The changes have been rolled back 
     assert Order.get(1).isActive 
     // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 
    } 
} 

最后,一些更一般的注解,它不是boolean transactional = true(它似乎工作,虽然),但static transactional = true。你的集成测试也应该extendGroovyTestCase,而不是它的子类GrailsUnitTestCase,因为你不需要后者的嘲讽能力。 isActive字段应该命名为active,然后通过命名约定自动生成isActive() getter。

+0

谢谢,所以看起来这是一个集成测试框架的错误。即代码/逻辑是否正确,并在正在运行的应用程序中工作,但不在集成测试中?也许我应该JIRA它 – Sunny 2010-11-12 06:00:45

+0

我不认为这是一个错误(这个问题太重要了/突出),而是一个设计缺陷。 - 如果您应该提交JIRA问题(使用Spring而不是Grails框架),请在此处留言。 – robbbert 2010-11-12 12:30:10

+0

对最后一个示例(过时?)的更正:'Propagation.REQUIRES_NEW'开始一个新的_unrelated和independent_事务。相比之下,'Propagation.NESTED'下出现_nested_事务(对父对象和其自己的保存点可见)。 Spring Javadoc做了很好的解释差异:http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/transaction/annotation/Propagation.html – 2015-02-05 14:45:00