2010-01-15 146 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筆記有一個解決方法,但我沒有嘗試過。但根據這個問題,它在運行應用程序時仍然可以工作。這可以手動確認。