根据JUnit 5用户指南,JUnit Jupiter为某些JUnit 4规则提供了向后兼容性,以帮助迁移。
如上所述,JUnit Jupiter 本身不支持 JUnit 4 规则。然而,JUnit团队意识到,许多组织,尤其是大型组织,可能拥有大型JUnit 4代码库,包括自定义规则。为了服务于这些组织并实现逐步迁移路径,JUnit 团队决定在 JUnit Jupiter 中逐字支持 JUnit 4 规则。
该指南继续说,其中一个规则是ExternalResource,它是TemporaryFolder的父级。
但是,不幸的是,该指南没有继续说明迁移路径是什么,或者对于那些编写新的JUnit 5测试的人来说,迁移路径是什么。那么我们应该使用什么呢?
JUnit5 TemporaryFolderExtension 作者的有趣文章
和
他在 GitHub 上的代码存储库
JUnit5.0.0 现在是正式发布的,所以让我们希望他们将注意力转向使实验性的东西为生产做好准备。
同时,似乎TemporaryFolder规则仍然适用于JUnit5文档。
使用这个:
@EnableRuleMigrationSupport
public class MyJUnit5Test {
而这个:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-migrationsupport</artifactId>
<version>5.0.0</version>
</dependency>
据我了解,在 JUnit5 中不可能有从 ExternalResource
到等效项的一对一映射。这些概念就是不合适。在 JUnit4 中,ExternalResource
基本上为您提供了一个before
和一个after
回调,但在规则中,您无法控制 before
和after
的实际含义。您可以将其与@Rule
或@ClassRule
一起使用。
在 JUnit5 中,扩展被定义为挂钩特定的扩展点,因此"何时"被很好地定义。
概念上的另一个区别是,你可以在 JUnit4 规则中有一个状态,但你的 JUnit5 扩展不应该有任何状态。相反,所有状态都应转到执行上下文。
尽管如此,这里有一个选项,其中before
和after
与每种测试方法有关:
public abstract class ExternalResourceExtension
implements BeforeTestExecutionCallback, AfterTestExecutionCallback {
@Override
public void beforeTestExecution(ExtensionContext context) throws Exception {
before(context);
}
@Override
public void afterTestExecution(ExtensionContext context) throws Exception {
after(context);
}
protected abstract void before(ExtensionContext context);
protected abstract void after(ExtensionContext context);
}
JUnit 5.4 带有一个内置扩展来处理测试中的临时目录。
@org.junit.jupiter.api.io.TempDir
注释可用于注释生命周期中的类字段或参数(例如 @BeforeEach
(或File
或Path
型的测试方法。
import org.junit.jupiter.api.io.TempDir;
@Test
void writesContentToFile(@TempDir Path tempDir) throws IOException {
// arrange
Path output = tempDir
.resolve("output.txt");
// act
fileWriter.writeTo(output.toString(), "test");
// assert
assertAll(
() -> assertTrue(Files.exists(output)),
() -> assertLinesMatch(List.of("test"), Files.readAllLines(output))
);
}
您可以在我的博客文章中阅读更多内容,您将在其中找到有关使用此内置扩展的更多示例:https://blog.codeleak.pl/2019/03/temporary-directories-in-junit-5-tests.html。
这方面的文档仍在制作中 - 请参阅拉取请求 #660。
临时文件夹现在有一个解决方案,可以@TempDir
. 但是,一般而言,ExternalResource
背后的想法呢? 也许它适用于模拟数据库、模拟 HTTP 连接或您想要添加支持的其他自定义资源?
事实证明,答案是您可以使用@RegisterExtension
注释来实现非常相似的东西。
使用示例:
/**
* This is my resource shared across all tests
*/
@RegisterExtension
static final MyResourceExtension MY_RESOURCE = new MyResourceExtension();
/**
* This is my per test resource
*/
@RegisterExtension
final MyResourceExtension myResource = new MyResourceExtension();
@Test
void test() {
MY_RESOURCE.doStuff();
myResource.doStuff();
}
这是MyResourceExtension
的基本脚手架:
public class MyResourceExtension implements BeforeAllCallback, AfterAllCallback,
BeforeEachCallback, AfterEachCallback {
private SomeResource someResource;
private int referenceCount;
@Override
public void beforeAll(ExtensionContext context) throws Exception {
beforeEach(context);
}
@Override
public void afterAll(ExtensionContext context) throws Exception {
afterEach(context);
}
@Override
public void beforeEach(ExtensionContext context) throws Exception {
if (++referenceCount == 1) {
// Do stuff in preparation
this.someResource = ...;
}
}
@Override
public void afterEach(ExtensionContext context) throws Exception {
if (--referenceCount == 0) {
// Do stuff to clean up
this.someResource.close();
this.someResource = null;
}
}
public void doStuff() {
return this.someResource.fooBar();
}
}
当然,您可以将所有这些包装为抽象类,并且MyResourceExtension
实现protected void before()
和protected void after()
之类的东西,如果这是您的事情,但为了简洁起见,我省略了它。