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