Как обрабатывать порядок @Rule, когда они зависят друг от друга

Я использую встроенные серверы, которые работают внутри тестовых случаев Junit. Иногда этим серверам требуется рабочий каталог (например, сервер Apache Directory).

Новое правило @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)


person Lennart Schedin    schedule 28.04.2010    source источник
comment
Как немного поправили порядок с 4.7 на 4.8?   -  person zedoo    schedule 24.04.2014


Ответы (4)


РЕДАКТИРОВАТЬ: с недавно выпущенным 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
person Matthew Farwell    schedule 22.09.2011
comment
А что, если я хочу получить доступ к правилу из теста? Есть ли способ получить доступ к содержимому поля chain? - person Henrik Aasted Sørensen; 10.11.2015
comment
Ничто не мешает вам объявить правило как приватное поле в вашем классе, а затем использовать его в RuleChain. Затем у вас будет доступ к нему в тесте - person Matthew Farwell; 11.11.2015
comment
Ах, конечно. Настолько просто, что я пропустил его в своем лихорадочном поиске доступа к классу. Спасибо. - person Henrik Aasted Sørensen; 11.11.2015
comment
К сожалению, RuleChain не принимает MehtodRule. - person Brice; 13.01.2017

Чтобы сделать правила зависимыми, вы должны сначала их инициализировать и создать отношения зависимости с помощью конструкторов или (в зависимости от вашего правила) быстрых строителей. Отношения зависимости должны быть определены при инициализации поля и не могут быть созданы в методах @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());
  }
  ...
}
person Gerald Mücke    schedule 02.12.2014

Если вы не найдете нормального решения, вы всегда можете создать составное правило (и единственное с аннотацией @Rule), которое содержит все остальные и выполняет их по порядку.

person Ha.    schedule 29.04.2010
comment
Да, это сработает. Его недостаток в том, что папка не может быть легко разделена между другим кодом в тестовом примере. - person Lennart Schedin; 04.05.2010

В качестве альтернативы вы можете просто предложить установщик в своем правиле MyNumberServer вместо того, чтобы указывать папку в конструкторе.

Кроме того, порядок среди правил не гарантируется так, как вы описали. Это может стать немного сложным, особенно если вам нужна связь между вашими правилами, см., например, Лучший способ регистрации исключений при сбое тестов (например, с использованием правила junit).

person DaveFar    schedule 22.09.2011