我使用在Junit测试用例中运行的嵌入式服务器。有时这些服务器需要一个工作目录(例如Apache Directory服务器)。
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();
}
}
}
在此代码中,文件夹作为参数发送到服务器,以便服务器可以创建工作目录来存储数据。但是这不起作用,因为Junit按照与文件中定义的规则相反的顺序处理规则。 TemporaryFolder规则不会在服务器规则之前执行。因此,TempraryFolder中的根文件夹将为null,从而导致相对于当前工作目录创建任何文件。
如果我颠倒了我的类中属性的顺序,我会收到编译错误,因为在定义变量之前我无法引用它。
我正在使用Junit 4.8.1(因为规则的排序在4.7版本中有所修正)
答案 0 :(得分:41)
编辑:使用最近发布的Junit 4.10,您可以使用@RuleChain正确链接规则(参见最后的内容)。
您可以在没有@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());
}
...
}
最干净的解决方案是制定复合规则,但上述情况应该有效。
编辑:使用最近发布的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);
}
}
写日志
starting outer rule
starting middle rule
starting inner rule
finished inner rule
finished middle rule
finished outer rule
答案 1 :(得分:7)
要使规则相关,您必须首先初始化它们并使用构造函数或(根据您的规则)流畅的构建器创建依赖关系。必须在字段初始化中定义依赖关系,并且不能在@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());
}
...
}
答案 2 :(得分:3)
如果您找不到正常的解决方案,您始终可以创建包含所有其他规则的复合规则(以及唯一具有@Rule
注释的规则)并按顺序执行它们。
答案 3 :(得分:0)
或者,您只需在MyNumberServer
规则中提供一个setter,而不是在构造函数中提供该文件夹。
此外,规则之间的排序并不能保证您所描述的方式。它可能会变得有点棘手,特别是当您想要在规则之间进行某些通信时,请参阅例如Best way of logging exceptions when tests fail (e.g. using a junit rule)。