Java 是否可以覆盖JUnit中的预期异常?

Java 是否可以覆盖JUnit中的预期异常?,java,exception,testing,junit,mockito,Java,Exception,Testing,Junit,Mockito,我最近发现JUnit>4.10允许使用@Rule和ExpectedException。由于我不擅长复制代码,我尝试了以下方法。为了更好地理解,我将它从几个测试缩减为这两个。MockitoJUnitRunner是故意的,尽管在小规模示例中没有使用它 pom.xml <dependencies> <!-- Test dependencies --> <dependency> <groupId>junit</groupId>

我最近发现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>
问题是,下面的测试没有像我预期的那样工作。我正在尝试的是,默认情况下,期望出现异常类型,并且在某些情况下运行正常的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
注释。但是,我希望有人能帮助我理解为什么这不起作用?

解决方案是覆盖设置方法

您也可以手动执行此操作:

@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");
        }
    }
请查找以下有趣的链接以了解更多信息:


祝你好运:)

解决方案是覆盖设置方法

您也可以手动执行此操作:

@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");
        }
    }
请查找以下有趣的链接以了解更多信息:


祝你好运:)

它没有像你期望的那样工作(没有双关语)的原因与
TestRule
s如何使用JUnit有关

实际上,测试框架检查测试用例中的
TestRule
实例,然后依次调用
TestRule.apply()
方法。此方法接受一个
语句
对象并返回一个
语句()
。您的测试用例对象最初被包装在一条语句中,该语句提供给第一条TestRule,它返回一条全新的
语句
包装原始的
语句
。因此,基本上,TestRule有机会调整原始测试用例,通常添加新功能。一旦框架完成了所有TestRule实例,它就会调用
语句.evaluate()
方法,就像它为任何没有附加任何TestRules的“标准”测试用例所做的那样

这里的关键是框架和
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,您有两个选项。第一种方法是将测试类或测试基类一分为二。一个套件应该用于预期IllegalArgumentException的测试,另一个套件应该用于不预期IllegalArgumentException的测试或具有预期的某种替代异常的测试。第二个是接受44个测试中固有的重复,这些测试必须明确声明它们期望异常,而只有4个测试不期望异常


虽然这在很大程度上取决于您的测试用例的工作方式,但是您可能能够通过JUnit
理论在某种程度上实现您想要的目标。

它不能像您期望的那样工作的原因(没有双关语)与
TestRule
如何使用JUnit有关

实际上,测试框架检查测试用例中的
TestRule
实例,然后依次调用
TestRule.apply()
方法。此方法接受一个
语句
对象并返回一个
语句()
。您的测试用例对象最初被包装在一条语句中,该语句提供给第一条TestRule,它返回一条全新的
语句
包装原始的
语句
。因此,基本上,TestRule有机会调整原始测试用例,通常添加新的测试用例