我最近发现了JUnit> 4.10允许使用@Rule
和ExpectedException
。由于我在复制代码方面不大,所以我尝试了以下方法。为了更好地理解,我将其从几个测试缩小到这两个。 MockitoJUnitRunner
是故意的,虽然它没有在小缩放示例中使用。
的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 :(得分:3)
它没有按预期工作的原因(没有双关语意)与TestRule
如何使用JUnit有关。
有效的是,测试框架检查TestRule
个实例的测试用例,然后依次调用每个实例上的TestRule.apply()
方法。此方法采用Statement
对象并返回Statement()
。您的测试用例对象最初包含在一个Statement中,该Statement被赋予第一个TestRule,它返回一个包含原始Statement
的全新 Statement
。因此,基本上,TestRule有机会适应原始TestCase,通常会添加新功能。一旦框架经历了所有TestRule实例,它就会调用Statement.evaluate()
方法,就像它建立的任何“标准”测试用例一样,它没有附加任何TestRule。
这里的关键是框架和TestRule
实例之间的所有交互都发生在测试用例构建时。一旦构建了测试用例,测试框架就不再查询包含规则的字段或直接与其进行交互。之后的主要目的是使测试与规则中包含的可变状态相互作用。因此,如果您在测试用例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个测试中固有的重复,这些测试必须明确声明它们期望异常,而只有4个测试没有。
有可能您可以通过某种方式使用JUnit Theories
来实现您的目标,尽管这在很大程度上取决于您的测试用例的工作方式。
答案 1 :(得分: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");
}
}
请查看以下有趣链接以了解更多信息:
祝你好运:)