2011-12-23 175 views
2

所以我有一個有趣的問題,我將需要一些幫助。我知道一些問題已經被問到使用junit進行事務回滾的問題,但我相信我的問題並且略有不同。爲了讓人們更好地理解這個問題,讓我從頭開始。Junit異常測試與春季交易和回滾

我已經爲用戶管理系統實施了用戶管理服務及其相應的DAO。有一種稱爲CreateUser(User obj)的通用方法,用於創建唯一用戶。現在,有一個約束條件,即電子郵件地址是唯一的,所以如果我們嘗試使用已經使用的電子郵件地址調用此方法,我們會拋出一個名爲UserManagementException的自定義異常及其相應的錯誤消息。所有這些工作都很好,但是我遇到的問題是單元測試。哦,差點忘了,讓我提我使用的軟件棧[的Java,spring,hibernate的]

我有我的單元測試類爲每個實際會達到分貝法事務的註解。這些方法還具有@Rollback註釋,以便在每次測試調用結束時回滾所有插入,更新和刪除操作。所以我在這裏面臨的問題是我想測試獨特的用戶約束場景。通過使用具有相同電子郵件地址的用戶對象第二次調用createUser(obj),我想確保拋出UserManagementException異常。但是,由於它是事務性的,所以無論何時拋出異常,事務在單元測試完成之前都會回滾,因此測試失敗。以下是測試用例。

@Test 
@Rollback 
@Transactional 
public void testUniqueCreateConsoleUser() { 
    boolean success; 
    ConsoleUser newUser; 
    //first one 
    userManagementDao.createConsoleUser(user); 
    //second one. This shd throw a UserManagementException 
    try { 
     //now try and insert a new user with same email 
     newUser = new ConsoleUser("Queen", "Kong", "[email protected]", "kingkong","Universal Studios", "America/Los_Angeles", false, null); 
     userManagementDao.createConsoleUser(newUser); 
     //if this passed this is a problem. Console users should have unique email address 
     success = false; 
    } catch (UserManagementException e) { 
     success = true; 
    } 
    Assert.assertTrue(success); 
} 

奇怪的是,當我通過調試運行它,在Assert.assertTrue()方法調用正確的,但最終的測試失敗。

我嘗試的另一件事是添加一個道具到@Transactional註釋。我添加了流動的@Transactional(noRollbackFor = UserManagementException.class),希望如果拋出異常,那麼回滾將不會被調用,而是在測試結束時調用。我可能會以錯誤的方式來處理這個問題,所以圍繞這種測試的任何想法或最佳實踐都會受到極大的指導。

注:下面是從堆棧跟蹤片斷..

org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only 
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:695) 
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:321) 
at org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$afterReturning$org_springframework_transaction_aspectj_AbstractTransactionAspect 

回答

0

從您的示例中很難分辨出來,但您似乎正在測試您的實際DAO實現。使用模擬實現或模擬框架來模擬DAO,而不是讓單元測試數據碰到實際的數據庫。然後,您可以操作以編程方式返回的數據,並將其轉換爲您想要的任何驗證場景。

+0

是的,你說得對,我正在使用實際的DAO impl,因爲我想看到它在行動。我曾考慮過使用模擬對象,但我改變了主意,以使用實際的impl代替。 – 2011-12-23 20:05:59

+0

我決定使用模擬對象方法。即使所有的答案都是有效的,我只能接受一個:-) – 2011-12-24 08:52:43

0

如果你能確認一個額外的回滾被拋出(例如 - 當春天確實插入,當它看到,它失敗了,確實它已經把事務回滾了嗎?),那麼你應該捕獲回滾,或者配置spring不要將事務重新回滾。

也就是說,顯然,spring正在實現的回滾與單元測試中預期的回滾衝突。這個回滾會混淆回滾註釋,從而在「單元測試/ Spring以太網」中引發意外拋出的異常。

簡單的解決方案:不要啓用此測試的自動回滾。測試並不總是非常優雅。

+0

Thanx爲您的答覆,但我不知道我明白你在說什麼。如果你看看上面的代碼,當我調用userManagementDao.createConsoleUser(user);時,首先插入它。然後,第二個插入,應該打破唯一的電子郵件約束是在try catch塊中完成的。你還表示你*認爲*測試沒有正確實施。你將如何實施這個測試? – 2011-12-23 19:36:01

+0

看來你剛剛改變了你的答案:)。讓我現在消化你的新答案... – 2011-12-23 19:37:52

0

而不是插入一個用戶,然後將使用相同的電子郵件地址,其他用戶,我建議第一裝載從數據庫中現有的用戶,然後試圖用相同的電子郵件地址已檢索的一個插入花葯。如果是的話,你只需要做:除非UserManagementException拋出

@Test(expected = UserManagementException.class) 
public void insert_duplicate_user() throws Exception { 

    // Read user from database 
    final ConsoleUser user = dao.load(...); 

    // Create new user with same email address. 
    final ConsoleUser newUser = new ConsoleUser (...); 
    newUser.setEmail(user.getEmail()); 

    // Write 
    dao.createConsoleUser(newUser); 

    /* 
    * If you get here, there is a problem with your DAO logic 
    * and a new user (with the same email was created). 
    * So, we need to clean that up 
    */ 

    // Delete new user 
    dao.deleteUser(newUser); 
} 

這會導致測試失敗。