2010-01-15 195 views
1

我试图让事务在Grails服务中工作,但我没有得到我期待的结果。如果我的假设是关闭的,有人可以告诉我我做错了什么吗?Grails服务交易

我的域类:

class Account { 

    static constraints = { 
    balance(min: 0.00) 
    } 

    String companyName 
    BigDecimal balance = 0.00 
    Boolean active = true 

    String toString() { 
    return "${companyName} : ${balance}" 
    } 
} 

我的服务:

class AccountService { 

    static transactional = true 

    def transfer(Account source, Account destination, amount) throws RuntimeException { 

    if (source.active && destination.active) { 
     source.balance -= amount 

     if (!source.save(flush: true)) { 
     throw new RuntimeException("Could not save source account.") 
     } else { 
     destination.balance += amount 

     if (!destination.save(flush: true)) { 
      throw new RuntimeException("Could not save destination account.") 
     } 
     } 
    } else { 
     throw new RuntimeException("Both accounts must be active.") 
    } 
    } 

    def someMethod(Account account) throws RuntimeException { 

    account.balance = -10.00 

    println "validated: ${account.validate()}" 

    if(!account.validate()) { 
     throw new RuntimeException("Rollback!") 
    } 
    } 
} 

我的单元测试: 进口grails.test *

class AccountServiceTests extends GrailsUnitTestCase { 

    def AccountService 

    protected void setUp() { 
    super.setUp() 
    mockDomain(Account) 
    AccountService = new AccountService() 
    } 

    protected void tearDown() { 
    super.tearDown() 
    } 

    void testTransactional() { 
    def account = new Account(companyName: "ACME Toy Company", balance: 2000.00, active: true) 

    def exception = null 

    try { 
     AccountService.someMethod(account) 
    } catch (RuntimeException e) { 
     exception = e 
    } 

    assert exception instanceof RuntimeException 

    println "exception thrown: ${exception.getMessage()}" 

    assertEquals 2000.00, account.balance 
    } 
} 

结果:

Testsuite: AccountServiceTests 
Tests run: 1, Failures: 1, Errors: 0, Time elapsed: 1.068 sec 
------------- Standard Output --------------- 
--Output from testTransactional-- 
validated: false 
exception thrown: Rollback! 
------------- ---------------- --------------- 
------------- Standard Error ----------------- 
--Output from testTransactional-- 
------------- ---------------- --------------- 

Testcase: testTransactional took 1.066 sec 
    FAILED 
expected:<2000.00> but was:<-10.00> 
junit.framework.AssertionFailedError: expected:<2000.00> but was:<-10.00> 
    at AccountServiceTests.testTransactional(AccountServiceTests.groovy:89) 
    at _GrailsTest_groovy$_run_closure4.doCall(_GrailsTest_groovy:203) 
    at _GrailsTest_groovy$_run_closure4.call(_GrailsTest_groovy) 
    at _GrailsTest_groovy$_run_closure2.doCall(_GrailsTest_groovy:147) 
    at _GrailsTest_groovy$_run_closure1_closure19.doCall(_GrailsTest_groovy:113) 
    at _GrailsTest_groovy$_run_closure1.doCall(_GrailsTest_groovy:96) 
    at TestApp$_run_closure1.doCall(TestApp.groovy:66) 
    at gant.Gant$_dispatch_closure4.doCall(Gant.groovy:324) 
    at gant.Gant$_dispatch_closure6.doCall(Gant.groovy:334) 
    at gant.Gant$_dispatch_closure6.doCall(Gant.groovy) 
    at gant.Gant.withBuildListeners(Gant.groovy:344) 
    at gant.Gant.this$2$withBuildListeners(Gant.groovy) 
    at gant.Gant$this$2$withBuildListeners.callCurrent(Unknown Source) 
    at gant.Gant.dispatch(Gant.groovy:334) 
    at gant.Gant.this$2$dispatch(Gant.groovy) 
    at gant.Gant.invokeMethod(Gant.groovy) 
    at gant.Gant.processTargets(Gant.groovy:495) 
    at gant.Gant.processTargets(Gant.groovy:480) 

我的期望:

当帐户被授予负平衡,它不应该验证(它没有),一个RuntimeException应甩(它是),并且账户应该回滚到之前的状态(余额:2000),这是它分崩离析的地方。

我在这里错过了什么?

回答

4

单元测试只是Groovy或Java类,所以没有Spring应用程序上下文,因此没有事务支持。你不得不嘲笑单元测试,但这不会测试事务性,只是模拟的质量。将它转换为一个集成测试,不调用新的服务,使用依赖注入:

class AccountServiceTests extends GroovyTestCase { 

    def AccountService 

    void testTransactional() { 
    def account = new Account(companyName: "ACME Toy Company", balance: 2000.00, 
           active: true) 
    account.save() 
    assertFalse account.hasErrors() 

    String message = shouldFail(RuntimeException) { 
     AccountService.someMethod(account) 
    } 

    println "exception thrown: $message" 

    assertEquals 2000.00, account.balance 
    } 
} 

注意实际的异常可能是一个包装例外的同时抛出异常作为它的原因。

+0

嘿伯特,完全有道理。唉,仍然没有运气。它仍然应该抛出异常,但它并没有回滚。 – Thody 2010-01-15 18:18:09

+2

现在,它是如何工作的。帐户实例的数据不会回滚,但更改不会持久。将'def sessionFactory'添加为依赖注入字段,并在最后一个assertEquals之前添加对'sessionFactory.currentSession.clear()'和'account = Account.get(account.id)'的调用以强制重新加载帐户实例并且测试会通过。 – 2010-01-16 07:14:48

+1

对于使用它为交易服务编写集成测试的人来说,只是一个快速提示。集成测试本身是事务性的,所以你的服务不会在新的事务中。这可能会导致意外的行为。解决方法是在集成测试中关闭事务(static transactional = false)。 – 2010-03-18 14:22:28

0

您运行的是哪个版本的Grails? v1.1.1有一个错误,其中transactional = true无法正常工作。

+0

运行v1.1.1。 – Thody 2010-01-17 13:10:16

+1

是的。尝试取出'transactional = true'。看看你是否有更好的结果。 或者,试试1.2。 – 2010-01-18 23:28:07

+0

http://jira.codehaus.org/browse/GRAILS-4644 – 2010-01-18 23:32:13

1

我试过了代码,但在集成测试中看到了同样的问题。我用Grails 1.2

根据Jira GRAILS-3765这是一个已知的问题,仍然是开放的。 (我不确定为什么它只在“1.1.x”出现很长一段时间时才会显示“影响版本1.0.4”)。

基于这些观点,我认为这是Grails中的一个bug。 Jira笔记有一个解决方法,但我没有尝试过。但根据这个问题,它在运行应用程序时仍然可以工作。这可以手动确认。