2010-04-28 2 views
30

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

+0

Как заказ был зафиксирован немного от 4.7 до 4.8? – zedoo

ответ

3

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

+0

Да, это сработает. Недостаток заключается в том, что папка не может быть легко передана между другим кодом в тестовом примере. –

0

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

Кроме того, порядок между правилами не гарантируется, как вы описали. Это может стать немного сложной задачей, особенно если вам нужна какая-то связь между вашими правилами, см., Например, Best way of logging exceptions when tests fail (e.g. using a junit rule).

35

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 
+0

А что, если я хочу получить доступ к правилу изнутри теста? Любой доступ к содержимому поля 'chain'? – Henrik

+2

Нет ничего, чтобы остановить объявление правила как частного поля в вашем классе, а затем использовать его в RuleChain. Тогда у вас будет доступ к нему в тесте –

+1

. Ах, конечно. Так просто, что я пропустил это в своем безумном поиске доступа к классу. Благодарю. – Henrik

5

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