2016-12-28 52 views
1

我最近發現JUnit> 4.10允許使用@RuleExpectedException。由於我對重複的代碼不是很大,我嘗試了以下方法。爲了更好地理解,我將它從幾個測試縮小到了這兩個測試。 MockitoJUnitRunner是故意的,雖然它沒有用在小規模的例子中。是否有可能在JUnit中覆蓋預期的異常?

的pom.xml

<dependencies> 
    <!-- Test dependencies --> 
    <dependency> 
    <groupId>junit</groupId> 
    <artifactId>junit</artifactId> 
    <version>4.11</version> 
    <scope>test</scope> 
    </dependency> 
    <dependency> 
    <groupId>org.mockito</groupId> 
    <artifactId>mockito-all</artifactId> 
    <version>1.10.19</version> 
    <scope>test</scope> 
    </dependency> 
</dependencies> 

TestBase

@RunWith(MockitoJUnitRunner.class) 
public class TestBase { 
    /** JUnit > 4.10 allows expected exception handling like this */ 
    @Rule 
    public ExpectedException exception = ExpectedException.none(); 

    @Before 
    public void setup() { 
    this.expectBadParam(); 
    } 

    protected void expectBadParam() { 
    this.exception.expect(NullPointerException.class); 
    } 
} 

的問題是,下面的測試沒有工作,我希望它。我正在嘗試的是默認期望的異常類型,並在某些情況下運行正常的JUnit測試。設置後,我無法重置預期的異常。

public class ExpectedExceptionTest extends TestBase { 
    @Test 
    public void error() { 
    throw new NullPointerException(); 
    } 

    @Test 
    public void success() { 
    this.exception = ExpectedException.none(); 
    // this should be a success 
    } 
} 

我已經通過複製在各方法的expectBadParam方法我期望的異常以及覆蓋在測試類的@Before註釋發現不同的解決方案。不過,我希望有人能幫助我理解爲什麼這不起作用?

+0

你應該改寫設置,或者不要繼承'TestBase' –

+0

當我想要避免代碼重複時,如何重寫設置(我真正實現了該設置)有什麼幫助?例如45個測試應該拋出一個錯誤的參數異常,而4個測試應該以正常的成功運行。 同樣的建議不要從'TestBase'繼承。有多個測試正在使用該超類。爲了更好的理解,我調整了這個例子的大小。 它仍然避免了我爲什麼不能使用JUnit中的ExpectedException重置爲預期的異常。 – Dr4gon

回答

3

像您期望它不工作的原因(沒有雙關語意)是關係到如何TestRule s的JUnit的工作。

實際上發生的情況是,測試框架檢查TestRule實例的測試用例,然後依次調用每個用例的TestRule.apply()方法。此方法需要一個Statement對象並返回Statement()。您的測試用例對象最初包裝在一個Statement中,併發送給第一個TestRule,它返回一個包含原始Statement的全新Statement。因此,基本上,TestRule被賦予了原來TestCase的機會,通常會增加新的功能。一旦框架經歷了所有的TestRule實例,它就會調用Statement.evaluate()方法,就像它爲任何「標準」測試用例構建的那樣,它沒有任何附加的TestRules。

這裏關鍵的是框架和實例之間的所有交互發生在測試用例構建時。測試用例一旦建立起來,包含這些規則的字段將不再被測試框架查詢或直接與之交互。其後的主要目的是讓測試與規則中包含的可變狀態進行交互。因此,如果您在測試用例success()中更改實例字段,那麼對規則的結果完全沒有影響,因爲預期爲IllegalArgumentException的規則已應用於測試用例。

對於TestRule實現有一個「典型」形狀。他們看起來像這樣...

public void apply(Statement base, Description description) { 
    return new Statement() { 
     public void evaluate() { 
      // some initialisation 
      try { 
       base.evaluate(); 
      } finally { 
       // some tidy up here 
      } 
     } 
    } 
} 

這裏TestRule得到運行一些代碼後測試用例完成的機會。這是ExpectedException的工作方式(儘管它也有一個'catch Exception(e)'塊)。在測試過程中,您可以調用規則實例上的方法,該規則實例在TestRule對象內構建狀態,然後在調用finally塊時使用該方法。因此,當您調用'exception.expect(IllegalArgumentException.class)`時,測試規則將匹配器存儲在列表中,並基本上使用該匹配器和您可能已經設置的其他任何其他設備匹配捕獲到的異常。當您重置測試用例中的實例字段時,原始實例中的所有狀態仍然存在,因此測試仍然失敗。

要做你想做的事情,你需要一種重置ExpectedException實例的內部狀態的方法。不幸的是,ExpectedException類沒有任何方法可以刪除已添加的期望。只有真正有可能增加預期。而且,說實話,這是一個很好的理由 - 你的測試應該在邏輯上進行分組,更細的細節被添加得越接近測試用例。 「重置」期望的行爲是刪除而不是添加細節的行爲,因此表明您的測試在邏輯上不夠好。如果測試套件的某些部分增加了一些期望,而另一部分刪除了部分/全部,則會產生可維護性困難。

如果你想在這裏使用ExpectedException,你有2個選項。首先是將測試類或測試基類分成兩部分。一個套件應該用於期望IllegalArgumentException的測試,另一套套件適用於那些沒有或者有某種他們期望的替代異常的測試。第二個是接受44次測試所固有的重複,這44次測試必須明確聲明他們期望有例外,而只有4次測試沒有。

雖然這很可能取決於測試用例的工作方式,但您可能可以通過某種方式實現JUnit Theories的目標。

+0

感謝您的啓發。詳細的解釋幫助我理解了爲什麼例如我可以添加更多的異常但不改變實例。另外'Theories'對於我的下一個測試看起來很有前途:D這是一個我已經用[Parametrized JUnit](https://github.com/junit-team/junit4/wiki/Parameterized-tests)來實現的類似概念。 – Dr4gon

0

的解決方案是重寫設置方法,

你也可以手動做到這一點:

@Test 
public void success() { 
    try{ 
     ... all your code 
    } catch (Exception e){ 
     // check your nested clauses 
     if(e.getCause() instanceof ExpectedException){ 
      // pass 
     } else { 
      Assert.fail("unexpected exception"); 
     } 
    } 

請找到婁有趣的鏈接詳細瞭解:

祝你好運:)

+0

我很困惑。我已經知道您的解決方案對於較舊的JUnit版本是可行的。在我看來''Test'註釋[reference](https://www.mkyong.com/unittest/junit-4-tutorial-2-expected-exception-test/)中還有'expected'值比你的解決方案更好。您的示例如何回答我如何使用JUnit中的ExpectedException?的問題? – Dr4gon

相關問題