【问题标题】:How to handle ordering of @Rule's when they are dependent on each other当@Rule相互依赖时如何处理它们的排序
【发布时间】:2010-04-28 14:30:53
【问题描述】:

我使用在 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 以文件中定义的相反顺序处理规则。临时文件夹规则不会在服务器规则之前执行。因此 TempraryFolder 中的根文件夹将为空,从而创建相对于当前工作目录的任何文件。

如果我颠倒类中属性的顺序,则会出现编译错误,因为在定义变量之前我无法引用它。

我使用的是 Junit 4.8.1(因为从 4.7 版本开始,规则的顺序已经修复了一点)

【问题讨论】:

  • 从 4.7 到 4.8 的排序是如何固定的?

标签: java junit junit4


【解决方案1】:

编辑:使用最近发布的 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

【讨论】:

  • 如果我想从测试内部访问规则怎么办?有什么方法可以访问chain 字段的内容?
  • 没有什么可以阻止您在类中将规则声明为私有字段,然后在 RuleChain 中使用它。然后你就可以在测试中访问它
  • 啊,当然。如此简单,以至于我在疯狂搜索课程访问时错过了它。谢谢。
  • 不幸的是 RuleChain 不接受MehtodRule
【解决方案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】:

    你也可以使用属性order从4.13开始:

    如果您想控制应用规则的顺序,可以使用 order()。

       public class ThreeRules {
           @Rule(order = 0)
           public LoggingRule outer = new LoggingRule("outer rule");
      
           @Rule(order = 1)
           public LoggingRule middle = new LoggingRule("middle rule");
      
           @Rule(order = 2)
           public LoggingRule inner = new LoggingRule("inner rule");
      
           // ...
       }
    

    【讨论】:

      【解决方案5】:

      或者,您可以简单地在 MyNumberServer 规则中提供一个 setter,而不是在构造函数中提供文件夹。

      此外,不能保证按照您描述的方式在规则中进行排序。它可能会变得有点棘手,尤其是当您想要在规则之间进行一些通信时,请参见例如Best way of logging exceptions when tests fail (e.g. using a junit rule)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-09-08
        • 1970-01-01
        • 1970-01-01
        • 2018-04-08
        • 2014-02-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多