在JUnit 4.10及以下版本中,可以将规则注释为@Rule和@ClassRule。这意味着规则在类之前/之后以及每次测试之前/之后被调用。这样做的一个可能原因是设置昂贵的外部资源(通过@ClassRule调用),然后便宜地重置它(通过@Rule调用)。
从JUnit 4.11开始,@ Rule字段必须是非静态的,而@ClassRule字段必须是静态的,因此上述内容不再可能。
有明确的解决方法(例如明确地将@ClassRule和@Rule职责分开到单独的规则中),但是必须强制使用两个规则似乎是一种耻辱。我简要介绍了使用@Rule并推断它是否是第一个/最后一个测试,但我不相信这些信息是可用的(至少,它不能直接在描述中使用)。
在JUnit 4.11的单个规则中是否有一种干净整洁的@ClassRule和@Rule功能组合方式?
谢谢, 罗文</ P>
答案 0 :(得分:12)
从JUnit 4.12开始(在撰写本文时尚未发布),可以使用@Rule
和@ClassRule
注释单个静态规则。
请注意,它必须是静态的 - 使用@Rule
和@ClassRule
注释的非静态规则仍然被视为无效(因为任何带注释的@ClassRule
在类级别工作,所以只有真的作为静态成员有意义。)
如果您对更多细节感兴趣,请参阅the release notes和my pull request。
答案 1 :(得分:5)
另一种可能的解决方法是声明静态@ClassRule并在声明非静态@Rule时使用该值:
@ClassRule
public static BeforeBetweenAndAfterTestsRule staticRule = new BeforeBetweenAndAfterTestsRule();
@Rule
public BeforeBetweenAndAfterTestsRule rule = staticRule;
这意味着您不必重构任何现有规则类,但仍需要声明两个规则,因此它不能很好地回答原始问题。
答案 2 :(得分:2)
另一种可行的解决方法是声明一个非静态的@Rule,并让它对静态协作者起作用:如果协作者尚未初始化,则@Rule知道它是第一次运行(因此它可以设置它的合作者,例如启动外部资源);如果它们被初始化,@ Rule可以进行每次测试工作(例如重置外部资源)。
这样做的缺点是@Rule不知道它何时处理了最后一次测试,所以不能执行任何后级操作(例如整理外部资源);但是,可以使用@AfterClass方法来执行此操作。
答案 3 :(得分:2)
问题的答案如下:没有干净的方法(仅同时设置两个规则)。 我们尝试为自动测试重试实现类似的任务,并且结合了两个规则(用于此任务)并且实现了这样丑陋的方法: Tests retry with two rules
但是如果更准确地考虑必要的任务(需要实现),可以使用jUnit自定义Runner来使用更好的方法: Retry Runner
因此,为了获得更好的方法,最好知道您的具体用例。
答案 4 :(得分:1)
我遇到过类似的问题,有两种解决方法。我不喜欢任何一个,但他们有不同的权衡:
1)
如果您的规则公开了清理方法,您可以在@Before
方法中手动调用清理。
@ClassRule
public static MyService service = ... ;
@Before
public void cleanupBetweenTests(){
service.cleanUp();
}
这样做的缺点是你需要记住(并告诉团队中的其他人)总是添加@Before方法或创建一个测试继承的抽象类来为你做清理。
2)有2个字段,一个是静态的,一个是非静态的,指向同一个对象,每个字段分别用@ClassRule
或@Rule
注释。如果未公开清理,则需要这样做。当然,缺点是你必须记住同时拥有@ClassRule和@Rule指向看起来很奇怪的东西。
@ClassRule
public static MyService service = ... ;
@Rule
public MyService tmp = service ;
然后在您的实现中,您必须区分测试套件或单个测试。这可以通过检查Description
是否有孩子来完成。根据哪一个,您可以创建不同的Statement
适配器来处理清理:
@Override
protected void after() {
//class-level shut-down service
shutdownService();
}
@Override
protected void before() {
//class-level init service
initService();
}
@Override
public Statement apply(Statement base, Description description) {
if(description.getChildren().isEmpty()){
//test level perform cleanup
return new CleanUpStatement(this,base);
}
//suite level no-change
return super.apply(base, description);
}
以下是每次测试前要清理的自定义Statement
类:
private static final class CleanUpStatement extends Statement{
private final MyService service;
private final Statement statement;
CleanUpStatement(MyService service, Statement statement) {
this.service = service;
this.statement = statement;
}
@Override
public void evaluate() throws Throwable {
//clear messages first
myService.cleanUp();
//now evaluate wrapped statement
statement.evaluate();
}
}
在完成所有这些之后,我会更倾向于选项1,因为它更具意图揭示并且维护的代码更少。我还担心其他人试图修改选项2中的代码,认为有一个错误,因为相同的字段指向两次。额外的努力,代码评论等都不值得。
无论如何,您仍然可以使用模板方法将锅炉盘复制并粘贴到任何地方或抽象类。