Как обрабатывать порядок @Rule, когда они зависят друг от друга
Я использую встроенные серверы, которые запускаются внутри тестов Junit. Иногда для этих серверов требуется рабочий каталог (например, сервер каталога Apache).
Новый @Rule в Junit 4.7 может обрабатывать эти случаи. 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 будет пустой, в результате чего будут созданы любые файлы относительно текущего рабочего каталога.
Если я отменяю порядок атрибутов в моем классе, я получаю ошибку компиляции, потому что я не могу ссылаться на переменную до ее определения.
Я использую Junit 4.8.1 (потому что упорядочение правил было немного исправлено из версии 4.7)
Ответы
Ответ 1
EDIT: с недавно выпущенным 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());
}
...
}
Самое чистое решение - иметь составное правило, но вышеприведенное должно работать.
EDIT: с недавно выпущенным 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
Ответ 2
Чтобы правила зависели, вам нужно сначала инициализировать их и создать отношения зависимости с помощью конструкторов или (в зависимости от вашего правила) свободных разработчиков. Отношения зависимостей должны быть определены в инициализации поля и не могут быть созданы в методах @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());
}
...
}
Ответ 3
Если вы не найдете нормального решения, вы всегда можете создать составное правило (и единственное, у которого есть аннотация @Rule
), который содержит все остальные и выполняет их по порядку.
Ответ 4
В качестве альтернативы вы можете просто предложить установщик в своем правиле MyNumberServer
вместо того, чтобы указывать папку в конструкторе.
Кроме того, порядок между правилами не гарантируется описанным вами способом. Это может стать немного сложной задачей, особенно если вам нужна какая-то связь между вашими правилами, см., Например, Лучший способ регистрации исключений при неудачных тестах (например, с использованием правила junit).