差異處
這裏顯示兩個版本的差異處。
java:junit:powermock:diffexpectresult [2013/02/25 13:38] 127.0.0.1 外部編輯 |
java:junit:powermock:diffexpectresult [2023/06/25 09:48] |
||
---|---|---|---|
行 1: | 行 1: | ||
- | {{tag>>java powermock}} | ||
- | ====== 不同的Expected Result ====== | ||
- | ===== Problem ===== | ||
- | 為了讓程式夠強健,在發生能夠處理的例外情況可能會做retry。在[[http://teddy-chen-tw.blogspot.tw/2010/03/5-spare-handler.html|搞笑談軟工]]有提到retry好的做法,也可以透過spring的[[http://static.springsource.org/spring-batch/apidocs/org/springframework/batch/retry/support/RetryTemplate.html|RetryTemplate]]去實做retry(很大包..)。\\ | ||
- | 但在測試的時候,要怎麼確認你的retry是否可以work呢? 讓我們來看看範例。這個範例在執行時,如果有例外產生的時候,會使用替代方案執行,如果超過重試次數就會拋出一個RuntimeException。 | ||
- | <code java> | ||
- | package org.tonylin.powermock; | ||
- | public class RetryExample { | ||
- | public User readUser(){ | ||
- | int attempt = 0; | ||
- | int maxAttempt = 2; | ||
- | boolean retry = false; | ||
- | User user = null; | ||
- | do { | ||
- | try { | ||
- | retry = false; | ||
- | if (attempt == 0) | ||
- | user = DaoUtil.readUserFromDB(); | ||
- | else | ||
- | user = DaoUtil.readUserFromFile(); | ||
- | } catch (Exception e) { | ||
- | attempt++; | ||
- | retry = true; | ||
- | if (attempt > maxAttempt) | ||
- | throw new RuntimeException(e); | ||
- | } | ||
- | } while (attempt <= maxAttempt && retry); | ||
- | return user; | ||
- | } | ||
- | } | ||
- | </code> | ||
- | 測試案例可以這樣設計:\\ | ||
- | * 第一次執行成功。 | ||
- | * 第一次失敗,改由readUserFromFile執行成功。 | ||
- | * 第一次執行失敗,第二次執行readUserFromFile失敗,第三次才成功。 | ||
- | * 執行皆失敗。 | ||
- | 該如何透過PowerMock去實做這些測試案例呢? | ||
- | ===== How to resolve? ===== | ||
- | **第一次執行成功**這個案例較基本,我就不特別說明。 | ||
- | ==== 宣告 ==== | ||
- | 首先我們要先宣告PowerMock相關的annotation: | ||
- | <code java> | ||
- | @RunWith(PowerMockRunner.class) | ||
- | @PrepareForTest({RetryExample.class, DaoUtil.class}) | ||
- | public class TestRetryExample { | ||
- | |||
- | } | ||
- | </code> | ||
- | ==== 第一次失敗,改由readUserFromFile執行成功 ==== | ||
- | 這個案例不難實做,首先讓readUserFromDB拋exception,readUserFromFile回傳User物件。需要驗證的內容,包含取得的物件必須與我們建立的mock物件吻合,還要確認程式先執行readUserFromDB再執行readUserFromFile。因此我在mock DaoUtil時,使用了**mockStaticStrict**,這會讓PowerMock.verifyAll()驗證流程必須依照我們所宣告的順序。 | ||
- | <code java> | ||
- | @Test | ||
- | public void testReadUser_failureAtFirstTime(){ | ||
- | User mockUser = PowerMock.createMock(User.class); | ||
- | PowerMock.mockStaticStrict(DaoUtil.class); | ||
- | |||
- | DaoUtil.readUserFromDB(); | ||
- | PowerMock.expectLastCall().andThrow(new RuntimeException()).once(); | ||
- | |||
- | DaoUtil.readUserFromFile(); | ||
- | PowerMock.expectLastCall().andReturn(mockUser).once(); | ||
- | |||
- | PowerMock.replayAll(); | ||
- | |||
- | RetryExample retryExample = new RetryExample(); | ||
- | User user = retryExample.readUser(); | ||
- | assertEquals(mockUser, user); | ||
- | PowerMock.verifyAll(); | ||
- | } | ||
- | </code> | ||
- | ==== 執行皆失敗 ==== | ||
- | 即所有的請求都拋exception。在驗證的部分要去catch exception,如果沒發生exception就代表有問題。 | ||
- | <code java> | ||
- | @Test | ||
- | public void testReadUser_failure3Times(){ | ||
- | PowerMock.mockStaticStrict(DaoUtil.class); | ||
- | |||
- | DaoUtil.readUserFromDB(); | ||
- | PowerMock.expectLastCall().andThrow(new RuntimeException()).once(); | ||
- | |||
- | DaoUtil.readUserFromFile(); | ||
- | PowerMock.expectLastCall().andThrow(new RuntimeException()).times(2); | ||
- | |||
- | PowerMock.replayAll(); | ||
- | |||
- | RetryExample retryExample = new RetryExample(); | ||
- | |||
- | try { | ||
- | retryExample.readUser(); | ||
- | fail(); | ||
- | } catch( Exception e ){ | ||
- | PowerMock.verifyAll(); | ||
- | } | ||
- | } | ||
- | </code> | ||
- | ==== 第一次執行失敗,第二次執行readUserFromFile失敗,第三次才成功 ==== | ||
- | 這才是這篇的重點。為了能模擬同一個method會有不同的執行結果,我使用了PowerMock.expectLastCall().andAnswer()並且override answer()。接著透過一個flag去記錄是第幾次執行以決定要回傳的結果。 | ||
- | <code java> | ||
- | @Test | ||
- | public void testReadUser_failure2Times(){ | ||
- | final User mockUser = PowerMock.createMock(User.class); | ||
- | PowerMock.mockStaticStrict(DaoUtil.class); | ||
- | |||
- | DaoUtil.readUserFromDB(); | ||
- | PowerMock.expectLastCall().andThrow(new RuntimeException()).once(); | ||
- | |||
- | DaoUtil.readUserFromFile(); | ||
- | PowerMock.expectLastCall().andAnswer(new IAnswer<User>() { | ||
- | private int mTimes = 0; | ||
- | @Override | ||
- | public User answer() throws Throwable { | ||
- | mTimes++; | ||
- | if( mTimes == 1 ){ | ||
- | throw new RuntimeException(); | ||
- | } | ||
- | return mockUser; | ||
- | } | ||
- | }).times(2); | ||
- | |||
- | PowerMock.replayAll(); | ||
- | |||
- | RetryExample retryExample = new RetryExample(); | ||
- | User user = retryExample.readUser(); | ||
- | assertEquals(mockUser, user); | ||
- | PowerMock.verifyAll(); | ||
- | } | ||
- | </code> | ||
- | |||
- | IAnswer其實還能取得輸入參數,好去回傳不同的結果,這部分等之後有機會再分享給大家。 | ||
- | ===== ===== | ||
- | ---- | ||
- | \\ | ||
- | ~~DISQUS~~ |