Junit @Rule是如何工作的?

236

我想为大量的代码编写测试用例,我希望了解JUnit@Rule注解特性的详细信息,以便我可以使用它来编写测试用例。请提供一些好的答案或链接,通过简单的示例详细描述其功能。


我已经查看了这个链接:http://cwd.dhemery.com/2011/01/what-junit-rules-are-good-for/。 - Dipak
1
我发现这篇文章非常好地解释了@Rule,特别是请注意最后一节“详细的事件序列”。 - Peter Perháč
1
我认为这类似于注入的概念,我是对的吗? - Chao
谢谢分享链接。有一件事不太清楚。DEFAULT Statement.evaluate() 什么时候被调用?是在所有规则的 evaluate() 之前还是之后?我猜是在所有规则之后。 - MasterJoe
@testerjoe2你可以选择完全忽略默认的语句。你可以选择委托它或者用自己的其他语句完全替换它。它不会被调用,你可以随意调用或不调用它。这在第10点中有说明:“屏幕截图语句的evaluate()方法调用了默认语句的evaluate()方法。” - Peter Perháč
我来这里是为了理解如何使用自定义规则(custom Rule)与“RetryRuleTest”一起工作。它来自于Junit和mockito书籍,你也读过。代码-https://github.com/tomekkaczanowski/junit-put-examples/tree/master/src/test/java/com/practicalunittesting/chp06/rules/custom。它是否像这样工作?(1) Junit创建一个默认语句并调用其evaluate()方法来运行测试方法。然后,Junit调用第一个(也是唯一的)规则的apply()方法。Junit运行由规则返回的语句的evaluate()方法。故事结束。 - MasterJoe
4个回答

171

规则被用来为测试类中的所有测试添加额外的功能,但以一种更通用的方式应用于所有测试。

例如,ExternalResource可以在测试方法之前和之后执行代码,无需使用@Before@After。使用ExternalResource而不是@Before@After可以更好地重用代码;相同的规则可以从两个不同的测试类中使用。

这个设计是基于:JUnit中的拦截器

有关更多信息,请参见JUnit wiki : Rules


1
更正:例如,ExternalResource在测试之前和之后执行代码。使用apply()的某些方法可以使ExternalResource在测试之间运行。 - derekm
阅读这篇文章帮助我理解答案中提到的ExternalResource - jumping_monkey

78

Junit规则基于AOP(面向切面编程)原理工作。它拦截测试方法,从而提供在特定测试方法执行之前或之后执行某些操作的机会。

以下面的代码为例:

public class JunitRuleTest {

  @Rule
  public TemporaryFolder tempFolder = new TemporaryFolder();

  @Test
  public void testRule() throws IOException {
    File newFolder = tempFolder.newFolder("Temp Folder");
    assertTrue(newFolder.exists());
  }
} 

每次执行以上测试方法时,都会创建一个临时文件夹,并在方法执行后将其删除。这是Junit提供的开箱即用规则的示例。

我们也可以通过创建自己的规则来实现类似的行为。Junit提供了TestRule接口,可以实现以创建我们自己的Junit规则。

以下是一个参考链接:


4
那么它会被删除,而不需要编写任何代码来删除/清除对象吗? - Dror
查看 https://github.com/junit-team/junit4/blob/master/src/main/java/org/junit/rules/TemporaryFolder.java 的源代码,文件夹是在 before() 回调方法中创建的,并在 after() 回调方法中删除... - Pierluigi Vernetto
1
对于可能不理解为什么TemporaryFolder会被删除的人来说,这是因为它是由Junit提供的临时文件夹,用作自动删除的临时文件夹 - 即拆卸步骤是TemporaryFolder类本身的一部分。 - Mindaugas Bernatavičius

27

它的工作原理如下:

JUnit会将您的测试方法封装在一个Statement对象中,使该语句和Execute()运行您的测试。然后,JUnit不直接调用statement.Execute()来运行您的测试,而是将Statement与@Rule注释一起传递给TestRule。TestRule的“apply”函数返回一个新的Statement,给出带有您的测试的Statement。新的Statement的Execute()方法可以调用测试Statement的execute方法(或不调用,或多次调用),并在之前和之后做任何想做的事情。

现在,JUnit有了一个新的Statement,不仅可以运行测试,还可以将其传递给任何更多的规则,最终调用Execute。


2
语句具有 evaluate 方法而非 execute。 - Hemanth

5

规则被用于以通用的方式增强每个测试方法的行为。JUnit规则拦截测试方法并允许我们在测试方法开始执行之前和测试方法执行完成后做一些事情。

例如,使用@Timeout规则,我们可以为所有测试设置超时时间。

public class TestApp {
    @Rule
    public Timeout globalTimeout = new Timeout(20, TimeUnit.MILLISECONDS);

    ......
    ......

 }

@TemporaryFolder 规则用于创建临时文件夹和文件。每次执行测试方法时,都会创建一个临时文件夹,并在方法执行后将其删除。

public class TempFolderTest {

 @Rule
 public TemporaryFolder tempFolder= new TemporaryFolder();

 @Test
 public void testTempFolder() throws IOException {
  File folder = tempFolder.newFolder("demos");
  File file = tempFolder.newFile("Hello.txt");

  assertEquals(folder.getName(), "demos");
  assertEquals(file.getName(), "Hello.txt");

 }


}

您可以在此链接中查看junit提供的一些内置规则示例。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接