What is the equivalent of ExternalResource and TemporaryFolder in JUnit 5?

后端 未结 5 2348
心在旅途
心在旅途 2021-02-19 02:20

According to the JUnit 5 User Guide, JUnit Jupiter provides backwards compatibility for some JUnit 4 Rules in order to assist with migration.

As stated ab

相关标签:
5条回答
  • 2021-02-19 03:04

    JUnit 5.4 comes with a built-in extension to handle temporary directories in tests.

    @org.junit.jupiter.api.io.TempDir annotation can be used in order to annotate class field or a parameter in a lifecycle (e.g. @BeforeEach) or test method of type File or 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))
        );
    }
    

    You can read more on this in my blog post, where you will find some more examples on utilizing this built-in extension: https://blog.codeleak.pl/2019/03/temporary-directories-in-junit-5-tests.html.

    0 讨论(0)
  • 2021-02-19 03:07

    Interesting article by author of TemporaryFolderExtension for JUnit5

    and

    his code repo on github

    JUnit5.0.0 is now in general release so let's hope they turn their attention to making the experimental stuff production-ready.

    Meanwhile, it seems the TemporaryFolder rule will still work with JUnit5 docs

    use this:

    @EnableRuleMigrationSupport
    public class MyJUnit5Test {
    

    and this:

    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-migrationsupport</artifactId>
        <version>5.0.0</version>
    </dependency>
    
    0 讨论(0)
  • 2021-02-19 03:11

    The documentation for that is still in the making - see pull request #660.

    0 讨论(0)
  • 2021-02-19 03:15

    Temporary folders now have a solution in the way of @TempDir. However, what about the idea behind ExternalResources in general? Perhaps it's for a mock database, a mock HTTP connection, or some other custom resource you want to add support for?

    The answer, it turns out is you can use the @RegisterExtension annotation to achieve something quite similar.

    Example of use:

    /**
     * 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();
    }
    

    And here's the basic scaffolding of 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();
        }
    
    }
    

    You could of course wrap this all up as an abstract class and have MyResourceExtension implement just protected void before() and protected void after() or some such, if that's your thing, but I'm omitting that for brevity.

    0 讨论(0)
  • 2021-02-19 03:22

    As far as I understood, there can be no one to one mapping from ExternalResource to an equivalent in JUnit5. The concepts just don't fit. In JUnit4, the ExternalResource basically gives you a before and an after callback, but within the rule, you have no control about what before and after actually means. You could use it with @Rule or with @ClassRule.

    In JUnit5, the extension is defined to hook in specific extension points and thus the 'when' is well defined.

    Another difference in concepts would be, that you can have a state in JUnit4 rules, but your JUnit5 extensions shouldn't have any state. Instead, all state should go to the execution context.

    Nevertheless, here is an option I came along with, where before and after relates to each test method:

    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);
    }
    
    0 讨论(0)
提交回复
热议问题