本文介绍了Grails集成测试和交易的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不明白为什么这个集成测试失败。我可以通过移除服务方法上方的 @Transactional(propagation = Propagation.REQUIRES_NEW)注释来传递测试,或者通过设置 transactional =假集成测试中的假



我意识到集成测试本身在事务中运行,这就是为什么我有注解

  class DbTests扩展GrailsUnitTestCase {

布尔transactional = true
def customerService

void testTransactionsCommit(){
def orderIds = [1,2,3]
orderIds.each {//确保它们都以Active $ b $开头b 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.i sActive
}
}

并定义了我的服务方法:

 类CustomerService {

布尔transactional = true
@Transactional(propagation = Propagation.REQUIRES_NEW)
def cancelOrders(def orderIds){
orderIds.each {
Order order = Order.get(it)
if(order.id == 5)//

order.isActive = false
order.save(flush:true,validate:true,failOnError:true)
println订购新的RuntimeException('在这里模拟一个异常,恐慌! .id = $ order.id是$ {order.isActive? 'ACTIVE':'CANCELED'}
}
}}

订单实体是一个简单的域对象,我在Grails 1.2.1,MySQL 5.x(dialect = org.hibernate.dialect.MySQL5InnoDBDialect)中。

看到这篇相关文章,但仍然没有雪茄:($ / b
$ b

解决方案

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



我真的不知道为什么它们在事务上下文中不是 GroovyTestCase 。。



考虑以下测试用例:测试用例本身是 $ b $ pre $ 类TransactionalMethodTest扩展GroovyTestCase {
static transactional = false //测试用例不是事务性的
def customerService

void testTransactionsCommit(){
//开始一个新的事务,
//设置命令1不活动
setOrderInactive()
断言! Order.get(1).isActive
}

@Transactional(propagation = Propagation.REQUIRED)
private void setOrderInactive(){
//确保订单1有效
Order order = Order.get(1)
order.isActive = true
order.save(flush:true)

assert Order.get 1).isActive

//以下方法以隔离级别运行
// Propagation.REQUIRES_NEW,表示
//一个新的嵌套事务启动
//>>>>>>>>>>>>>>>>>>>>>>>>>>>> >>>>>>>>>>>>>>>>>
customerService.cancelOrders([1])$ ​​b
$ b //嵌套事务的变化是
//可见,立即
断言! Order.get(1).isActive
//<<<<<<<<<<<<<<<<<<<< <<<<<<<<<<<<<<<<<<<<<<




$ b现在考虑下面的正常事务性的,测试用例。嵌套事务中的数据更改在父事务中不可见



我可以说的是,事务测试用例不会不适用于嵌套事务,所以使用上面的非事务性测试用例

如果我们不明白原因,我们至少可以知道我们的选项。

 类TransactionalTestCaseTests扩展GroovyTestCase {
static transactional = true // default; Propagation.REQUIRED
def customerService

void testTransactionsCommit(){
//确保订单1有效
订单订单= Order.get(1)
order.isActive = true
order.save(flush:true)

assert Order.get(1).isActive

//下面的方法动作隔离级别
// Propagation.REQUIRES_NEW,表示
//新的嵌套事务启动
//>>>>>>> >>>>>>>>>>>>>>>>>>>>>>>>> >>>>>>>>
customerService.cancelOrders([1])$ ​​b
$ b //内部事务
//的更改不可见
断言Order.get(1)。 isActive
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ;<<<<<<<<<<<<<<<<<<
}

@Override
保护无效tearDown()抛出异常{
//内部事务
//的更改仍然不可见
assert Order.get(1).isActive

super.tearDown();


$ / code $ / pre

$ hr

与您的主要问题无关,但是对于您的总体意图,这里是一个测试用例,它检查嵌套事务是否被正确回滚:

  class NestedTransactionRolledBackTests extends GroovyTestCase {
static transactional = false //测试用例不是事务性
def customerService

void testTransactionsCommit(){
// start一个新的交易
//设置订单1活动
setOrderActive()
断言Order.get(1).isActive
}

@Transactional(传播= Propagation.REQUIRED)
private void setOrderActive(){
//确保订单1有效
订单= Order.get(1)
order.isActive = true
order.save(flush:true)

assert Order.get(1).isActive

//以下方法用于隔离n级
// Propagation.REQUIRES_NEW,表示
//开始一个新的嵌套事务。
//此事务将失败,并回滚。
//>>>>>>>>>>>>>>>>>>>>>>>>>>> >>>>>>>>>>>>>>>>>>
shouldFail(NullPointerException){
customerService.cancelOrders([1,-999])
}

//嵌套事务的变化是
/ /立即可见。
//变更已回滚
assert Order.get(1).isActive
//< ;<<<<<<<<<<<<<<<<<<<<<<<<< ;<<<<<


$ / code $ / pre

$ hr

最后,还有一些更通用的注解,它不是 boolean transactional = true (虽然它似乎有效),但是 static transactional = true 。您的集成测试也应该 extend GroovyTestCase ,而不是它的子类 GrailsUnitTestCase ,因为你不需要后者的模拟功能。 isActive 字段应该命名为 active ,然后, isActive() getter将通过命名约定自动生成。


I don't get why this integration test fails. I can get the test to pass by either removing the @Transactional(propagation = Propagation.REQUIRES_NEW) annotation above the service method, OR by setting transactional = false in the Integration Test

I realize that the integration test itself is running in a transaction, and that's why I've got the annotation on the service method.

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

and my service method is defined:

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'}"
    }
}}

The Order entity is a simple domain object and I'm on Grails 1.2.1, MySQL 5.x (dialect=org.hibernate.dialect.MySQL5InnoDBDialect)

I've seen this related post, but still no cigar :(

Grails Service Transactions

解决方案

Data changes that a nested, inner, transaction had committed, should be, indeed, instantly visible in the parent transaction.

And I really don't know why they are not in the transactional context of a GroovyTestCase. Others don't know, as well, and are using similar approaches to mine.

Consider the following test case. The test case, itself, is not transactional, but calls a transactional method. - This works as expected.

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

Now consider the following, "normal", transactional, test case. Data changes from within the nested transaction are not visible in the parent transaction.

All I can say is, transactional test cases don't work with nested transactions, so use the non-transactional test case above.
If we don't understand the cause, we can, at least, know our options.

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


Not related to your primary question, but to your overall intent, here is a test case that checks whether the nested transaction is rolled back, properly:

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


Finally, some more general sidenotes, it's not boolean transactional = true (which appears to work, though), but static transactional = true. Your integration tests should also extend GroovyTestCase, not its subclass GrailsUnitTestCase, as you don't need the latter's mocking capabilities. The isActive field should be named active as then, an isActive() getter will be automatically generated by naming convention.

这篇关于Grails集成测试和交易的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-11 05:12