java 当@Rule 相互依赖时如何处理它们的排序
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2730365/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to handle ordering of @Rule's when they are dependent on each other
提问by Lennart Schedin
I use embedded servers that run inside Junit test cases. Sometimes these servers require a working directory (for example the Apache Directory server).
我使用在 Junit 测试用例中运行的嵌入式服务器。有时这些服务器需要一个工作目录(例如 Apache Directory 服务器)。
The new @Rule in Junit 4.7 can handle these cases. The TemporaryFolder-Rule can create a temporary directory. A custom ExternalResource-Rule can be created for server. But how do I handle if I want to pass the result from one rule into another:
Junit 4.7 中的新@Rule 可以处理这些情况。TemporaryFolder-Rule 可以创建一个临时目录。可以为服务器创建自定义 ExternalResource-Rule。但是,如果我想将结果从一个规则传递到另一个规则,我该如何处理:
import static org.junit.Assert.assertEquals;
import java.io.*;
import org.junit.*;
import org.junit.rules.*;
public class FolderRuleOrderingTest {
@Rule
public TemporaryFolder folder = new TemporaryFolder();
@Rule
public MyNumberServer server = new MyNumberServer(folder);
@Test
public void testMyNumberServer() throws IOException {
server.storeNumber(10);
assertEquals(10, server.getNumber());
}
/** Simple server that can store one number */
private static class MyNumberServer extends ExternalResource {
private TemporaryFolder folder;
/** The actual datafile where the number are stored */
private File dataFile;
public MyNumberServer(TemporaryFolder folder) {
this.folder = folder;
}
@Override
protected void before() throws Throwable {
if (folder.getRoot() == null) {
throw new RuntimeException("TemporaryFolder not properly initialized");
}
//All server data are stored to a working folder
File workingFolder = folder.newFolder("my-work-folder");
dataFile = new File(workingFolder, "datafile");
}
public void storeNumber(int number) throws IOException {
dataFile.createNewFile();
DataOutputStream out = new DataOutputStream(new FileOutputStream(dataFile));
out.writeInt(number);
}
public int getNumber() throws IOException {
DataInputStream in = new DataInputStream(new FileInputStream(dataFile));
return in.readInt();
}
}
}
In this code the folder is sent as a parameter into the server so that the server can create a working directory to store data. However this does not work because Junit processes the rules in reverse order as they are defined in the file. The TemporaryFolder Rule will not be executed before the server Rule. Thus the root-folder in TempraryFolder will be null, resulting that any files are created relative to the current working directory.
在此代码中,文件夹作为参数发送到服务器,以便服务器可以创建工作目录来存储数据。但是,这不起作用,因为 Junit 按照文件中定义的相反顺序处理规则。临时文件夹规则不会在服务器规则之前执行。因此,TempraryFolder 中的根文件夹将为空,导致任何文件都是相对于当前工作目录创建的。
If I reverse the order of the attributes in my class I get a compile error because I cannot reference a variable before it is defined.
如果我颠倒类中属性的顺序,则会出现编译错误,因为在定义变量之前我无法引用它。
I'm using Junit 4.8.1 (because the ordering of rules was fixed a bit from the 4.7 release)
我正在使用 Junit 4.8.1(因为规则的顺序从 4.7 版本开始修复了一点)
回答by Matthew Farwell
EDIT: With the recently released Junit 4.10, you can use RuleChainto chain rules correctly (see at the end).
编辑:在最近发布的 Junit 4.10 中,您可以RuleChain正确地使用链式规则(见文末)。
You could introduce another private field without the @Rule annotation, then you can reorder your code as you wish:
您可以在没有 @Rule 注释的情况下引入另一个私有字段,然后您可以根据需要重新排序代码:
public class FolderRuleOrderingTest {
private TemporaryFolder privateFolder = new TemporaryFolder();
@Rule
public MyNumberServer server = new MyNumberServer(privateFolder);
@Rule
public TemporaryFolder folder = privateFolder;
@Test
public void testMyNumberServer() throws IOException {
server.storeNumber(10);
assertEquals(10, server.getNumber());
}
...
}
The cleanest solution is to have a compound rule, but the above should work.
最干净的解决方案是有一个复合规则,但以上应该有效。
EDIT: With the recently released Junit 4.10, you can use RuleChainto chain rules correctly:
编辑:使用最近发布的 Junit 4.10,您可以RuleChain正确地使用链式规则:
public static class UseRuleChain {
@Rule
public TestRule chain = RuleChain
.outerRule(new LoggingRule("outer rule"))
.around(new LoggingRule("middle rule"))
.around(new LoggingRule("inner rule"));
@Test
public void example() {
assertTrue(true);
}
}
writes the log
写日志
starting outer rule
starting middle rule
starting inner rule
finished inner rule
finished middle rule
finished outer rule
回答by Gerald Mücke
To make the rules dependent, your have to initialize them first and create the dependency relationships using contructors or (depending on you rule) fluent builders. The dependency relations have to be defined in the field-initialization and could not be created in @Before methods as those are executed after rule application. To force the correct ordering of rule execution, you have to define the rule chain.
要使规则依赖,您必须首先初始化它们并使用构造函数或(取决于您的规则)流畅的构建器创建依赖关系。依赖关系必须在字段初始化中定义,并且不能在 @Before 方法中创建,因为这些是在规则应用之后执行的。要强制规则执行的正确顺序,您必须定义规则链。
public class FolderRuleOrderingTest {
private TemporaryFolder folder = new TemporaryFolder();
//assume, we have a rule that creates a testfile in a temporary folder
//we create a dependency relationship between file and folder,
//so that file depends on folder
private TemporaryFile file = new TemporaryFile(folder, "testfile.txt");
//the rule chain ensures, the temporary folder is created before and removed
//after the testfile has been created and deleted (or whatever)
@Rule
public RuleChain chain= RuleChain.outerRule(folder).around(file));
@Test
public void testFileExist() throws IOException {
assertTrue(file.getFile().exist());
}
...
}
回答by Ha.
If you will not find normal solution, you can always create compound rule (and the only one having @Ruleannotation) that contains all others and executes them in order.
如果您找不到正常的解决方案,您始终可以创建@Rule包含所有其他规则的复合规则(并且唯一具有注释的规则)并按顺序执行它们。
回答by DaveFar
Alternatively, you could simply offer a setter in your MyNumberServerrule instead of giving the folder in the constructor.
或者,您可以简单地在MyNumberServer规则中提供一个 setter,而不是在构造函数中提供文件夹。
Furthermore, ordering amongst rules is not guaranteed the way you described. It can become a little tricky, especially when you want some communication between your rules, see e.g. Best way of logging exceptions when tests fail (e.g. using a junit rule).
此外,不能保证按照您描述的方式在规则中排序。它可能会变得有点棘手,尤其是当您想要在规则之间进行一些通信时,请参阅例如测试失败时记录异常的最佳方式(例如使用 junit 规则)。

