Как обработать порядок @ правил, когда они зависят друг от друга - PullRequest
32 голосов
/ 28 апреля 2010

Я использую встроенные серверы, которые работают внутри тестовых случаев 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)

Ответы [ 4 ]

41 голосов
/ 22 сентября 2011

РЕДАКТИРОВАТЬ: С недавно выпущенным 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
7 голосов
/ 02 декабря 2014

Чтобы сделать правила зависимыми, вы должны сначала их инициализировать и создать отношения зависимостей, используя конструкторы или (в зависимости от вашего правила) беглые компоновщики. Отношения зависимости должны быть определены в инициализации поля и не могут быть созданы в методах @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 голосов
/ 29 апреля 2010

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

0 голосов
/ 22 сентября 2011

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

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

...