6

我不明白为什么这个集成测试失败了。我可以通过删除@Transactional(propagation = Propagation.REQUIRES_NEW)服务方法上方的注释或transactional = false在集成测试中进行设置来使测试通过

我意识到集成测试本身是在事务中运行的,这就是我在服务方法上添加注释的原因。

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 (dialect=org.hibernate.dialect.MySQL5InnoDBDialect)

我看过这个相关的帖子,但仍然没有雪茄:(

Grails 服务事务

4

1 回答 1

8

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

而且我真的不知道为什么它们不在. 其他人也不知道,并且正在使用类似的方法来挖掘.GroovyTestCase

考虑以下测试用例。测试用例本身不是事务性的,而是调用事务性方法。- 这按预期工作。

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. 您的集成测试也应该extend GroovyTestCase,而不是它的子类GrailsUnitTestCase,因为您不需要后者的模拟功能。该isActive字段应命名active为 then,isActive()将按照命名约定自动生成一个 getter。

于 2010-11-10T11:32:41.513 回答