问题 JUnit @Rule与@Before的生命周期交互


我有一些使用的JUnit测试 TemporaryFolder  @Rule。他们用的是 TemporaryFolder 在一个 @Before 执行某些设置的方法:

@Rule
public TemporaryFolder folder = new TemporaryFolder();

@Before
public void init() {
  folder.newFile("my-file.txt");
}

@Test
public void myTest() { ... }

大部分时间这都很完美。但是,使用时 SpringJUnit4ClassRunner 我发现在某些情况下 init() 方法在之前被调用 Statement 我的内心 TemporaryFolder 实例已应用。因此,临时文件夹位置未设置(即: null) 什么时候 folder 在...中使用 init() 我的文件最终在工作目录中,而不是 /tmp

所以在某些情况下 @Before 方法在规则之前执行,但是,我无法建立确定的模式。我偶尔会看到一些与我自己的规则实现类似的问题。

有什么方法可以确保在任何设置方法之前应用我的规则语句?


9316
2017-10-12 10:48


起源



答案:


在JUnit 4.10中,BlockJUnit4ClassRunner(SpringJUnit4ClassRunner的超类)似乎注意构造Statement链,使得规则在任何@Before方法之前运行。从JUnit 4.10:

protected Statement methodBlock(FrameworkMethod method) {
    // ...
    Statement statement= methodInvoker(method, test);
    statement= possiblyExpectingExceptions(method, test, statement);
    statement= withPotentialTimeout(method, test, statement);
    statement= withBefores(method, test, statement);
    statement= withAfters(method, test, statement);
    statement= withRules(method, test, statement);
    return statement;
}

JUnit 4.7似乎以不同的顺序将Statement链接在一起:

Statement statement= methodInvoker(method, test);
statement= possiblyExpectingExceptions(method, test, statement);
statement= withPotentialTimeout(method, test, statement);
statement= withRules(method, test, statement);
statement= withBefores(method, test, statement);
statement= withAfters(method, test, statement);
return statement;

spring-test-3.0.5的父POM似乎表明它依赖于JUnit 4.7。我想知道是否使用更新的JUnit会有所帮助吗?


12
2017-10-14 15:14



很好找@pholser。我正在使用JUnit 4.8.x,它确实以4.10的顺序构造链。但是,在SpringJUnit4ClassRunner中重写了methodBlock(),并设置了与JUnit 4.7类似的不同顺序。 - teabot


答案:


在JUnit 4.10中,BlockJUnit4ClassRunner(SpringJUnit4ClassRunner的超类)似乎注意构造Statement链,使得规则在任何@Before方法之前运行。从JUnit 4.10:

protected Statement methodBlock(FrameworkMethod method) {
    // ...
    Statement statement= methodInvoker(method, test);
    statement= possiblyExpectingExceptions(method, test, statement);
    statement= withPotentialTimeout(method, test, statement);
    statement= withBefores(method, test, statement);
    statement= withAfters(method, test, statement);
    statement= withRules(method, test, statement);
    return statement;
}

JUnit 4.7似乎以不同的顺序将Statement链接在一起:

Statement statement= methodInvoker(method, test);
statement= possiblyExpectingExceptions(method, test, statement);
statement= withPotentialTimeout(method, test, statement);
statement= withRules(method, test, statement);
statement= withBefores(method, test, statement);
statement= withAfters(method, test, statement);
return statement;

spring-test-3.0.5的父POM似乎表明它依赖于JUnit 4.7。我想知道是否使用更新的JUnit会有所帮助吗?


12
2017-10-14 15:14



很好找@pholser。我正在使用JUnit 4.8.x,它确实以4.10的顺序构造链。但是,在SpringJUnit4ClassRunner中重写了methodBlock(),并设置了与JUnit 4.7类似的不同顺序。 - teabot


对于它的价值,我使用以下作为快速解决方法:

@Rule
public TemporaryFolder tmpFolder = new TemporaryFolder() {
    @Override
    protected void before() throws Throwable {
        if (getRoot() == null) {
            super.before();
        }
    }

    @Override
    public File newFile(String fileName) throws IOException {
        try {
            before();
        }
        catch (Throwable t) {
            throw new RuntimeException(t.getMessage(), t);
        }

        return super.newFile(fileName);
    }

    @Override
    public File newFolder(String folderName) {
        try {
            before();
        }
        catch (Throwable t) {
            throw new RuntimeException(t.getMessage(), t);
        }

        return super.newFolder(folderName);
    }
};

这确保了 TemporaryFolder 无论是否正确初始化 @Before 方法在规则之前或之后运行。


0
2017-07-09 19:32