diff --git a/docs/temporaryFolder.md b/docs/temporaryFolder.md index 162e591..951f5a0 100644 --- a/docs/temporaryFolder.md +++ b/docs/temporaryFolder.md @@ -11,7 +11,7 @@ This extension is engaged by adding the `@ExtendWith` annotation to a test class #### Examples -###### Class Level TemporaryFolder +###### Instance Variable TemporaryFolder ``` @ExtendWith(TemporaryFolderExtension.class) @@ -19,6 +19,11 @@ public class MyTest { private TemporaryFolder temporaryFolder; + @BeforeEach + public void prepare(TemporaryFolder temporaryFolder) { + this.temporaryFolder = temporaryFolder; + } + @Test public void canUseTemporaryFolder() throws IOException { // use the temporary folder itself @@ -56,3 +61,32 @@ public class MyTest { } } ``` + +###### Class Variable TemporaryFolder + +``` +@ExtendWith(TemporaryFolderExtension.class) +public class MyTest { + + private static TemporaryFolder TEMPORARY_FOLDER; + + @BeforeAll + public void prepare(TemporaryFolder givenTemporaryFolder) { + this.TEMPORARY_FOLDER = givenTemporaryFolder; + } + + @Test + public void canUseTemporaryFolder() throws IOException { + // use the temporary folder itself + File root = TEMPORARY_FOLDER.getRoot(); + + // create a file within the temporary folder + File file = TEMPORARY_FOLDER.createFile("foo.txt"); + assertThat(file.exists(), is(true)); + + // create a directory within the temporary folder + File dir = TEMPORARY_FOLDER.createDirectory("bar"); + assertThat(dir.exists(), is(true)); + } +} +``` \ No newline at end of file diff --git a/pom.xml b/pom.xml index 940db0b..44650b7 100644 --- a/pom.xml +++ b/pom.xml @@ -53,18 +53,18 @@ 1.8 UTF-8 - 1.0.1 - 5.0.1 + 1.2.0 + 5.2.0 1.3 2.7.19 3.7.0 - 2.19 + 2.22.0 3.0.2 3.0.0-M1 4.3.0 0.7.9 - 3.0.1 + 3.0.0 diff --git a/src/main/java/io/github/glytching/junit/extension/folder/TemporaryFolder.java b/src/main/java/io/github/glytching/junit/extension/folder/TemporaryFolder.java index 65b8afb..2d18b3a 100644 --- a/src/main/java/io/github/glytching/junit/extension/folder/TemporaryFolder.java +++ b/src/main/java/io/github/glytching/junit/extension/folder/TemporaryFolder.java @@ -16,6 +16,8 @@ */ package io.github.glytching.junit.extension.folder; +import org.junit.jupiter.api.extension.ExtensionContext.Store.CloseableResource; + import java.io.File; import java.io.IOException; import java.nio.file.*; @@ -28,8 +30,8 @@ * with the operations which a tester may wish to invoke ({@link #createFile(String)}, {@link * #createDirectory(String)}) and post test invocations which the associated extension will invoke. */ -@SuppressWarnings("ResultOfMethodCallIgnored") -public class TemporaryFolder { +@SuppressWarnings({"ResultOfMethodCallIgnored", "nls"}) +public class TemporaryFolder implements CloseableResource { private static final String FILE_PREFIX = "junit"; private static final String FILE_SUFFIX = ".tmp"; @@ -53,6 +55,11 @@ public class TemporaryFolder { } } + @Override + public void close() throws Throwable { + destroy(); + } + /** * Returns the root folder. Exposing this offers some back compatability with JUnit4's {@code * TemporaryFolder} so test cases which are used to invoking {@code getRoot()} on a JUnit4 rule @@ -129,8 +136,10 @@ private FileVisitResult delete(Path file) throws IOException { } }); - // delete the parent - Files.delete(rootFolder.toPath()); + if (rootFolder.exists()) { + // delete the parent, if it still exists + Files.delete(rootFolder.toPath()); + } } } } diff --git a/src/main/java/io/github/glytching/junit/extension/folder/TemporaryFolderExtension.java b/src/main/java/io/github/glytching/junit/extension/folder/TemporaryFolderExtension.java index ea626f3..8893149 100644 --- a/src/main/java/io/github/glytching/junit/extension/folder/TemporaryFolderExtension.java +++ b/src/main/java/io/github/glytching/junit/extension/folder/TemporaryFolderExtension.java @@ -16,18 +16,22 @@ */ package io.github.glytching.junit.extension.folder; -import org.junit.jupiter.api.extension.*; - -import static io.github.glytching.junit.extension.util.ExtensionUtil.getStore; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ExtensionContext.Namespace; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolutionException; +import org.junit.jupiter.api.extension.ParameterResolver; /** * The temporary folder extension provides a test with access to temporary files and directories. * The temporary folder extension provides a {@link TemporaryFolder} which you can use to create a * temporary file or directory for use by your test. The {@link TemporaryFolder} can be injected - * into your test or test case with either of the following approaches: + * into your test or test case with any of the following approaches: * * + *
  • Class variable injection using a {@code @BeforeAll} method. Note: in this case all + * tests in the test case will share the same instance of the {@code TemporaryFolder}. The + * {@link TemporaryFolder} will be destroyed after any {@code @AfterAll} method completes and + * no exception will be thrown in cases where the deletion fails. For example: + *
    + *  private static TemporaryFolder TEMPORARY_FOLDER;
      *
    - * 

    In both approaches the {@link TemporaryFolder} will be destroyed during {@code @AfterEach} and - * no exception will be thrown in cases where the deletion fails. + * @BeforeAll + * public static void setUp(TemporaryFolder givenTemporaryFolder) { + * TEMPORARY_FOLDER = givenTemporaryFolder + * // ... + * } + *

    + * * *

    Usage examples: * @@ -112,29 +128,9 @@ * TemporaryFolder Rule * @since 1.0.0 */ -public class TemporaryFolderExtension implements AfterEachCallback, ParameterResolver { - - private static final String KEY = "temporaryFolder"; +public class TemporaryFolderExtension implements ParameterResolver { - /** - * If there is a {@link TemporaryFolder} associated with the current {@code extensionContext} then - * destroy it. - * - * @param extensionContext the context in which the current test or container is being - * executed - */ - @Override - public void afterEach(ExtensionContext extensionContext) { - TemporaryFolder temporaryFolder = - getStore(extensionContext, this.getClass()).get(KEY, TemporaryFolder.class); - if (temporaryFolder != null) { - try { - temporaryFolder.destroy(); - } catch (Exception e) { - // silent failures - } - } - } + private static final Namespace NAMESPACE = Namespace.create(TemporaryFolderExtension.class); /** * Does this extension support injection for parameters of the type described by the given {@code @@ -168,8 +164,10 @@ public boolean supportsParameter( public Object resolveParameter( ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { - return getStore(extensionContext, this.getClass()) - .getOrComputeIfAbsent(KEY, key -> new TemporaryFolder()); + return extensionContext + .getStore(NAMESPACE) + .getOrComputeIfAbsent( + parameterContext, key -> new TemporaryFolder(), TemporaryFolder.class); } private boolean appliesTo(Class clazz) { diff --git a/src/test/java/io/github/glytching/junit/extension/folder/TemporaryFolderExtensionBeforeAllTest.java b/src/test/java/io/github/glytching/junit/extension/folder/TemporaryFolderExtensionBeforeAllTest.java new file mode 100644 index 0000000..db8ee87 --- /dev/null +++ b/src/test/java/io/github/glytching/junit/extension/folder/TemporaryFolderExtensionBeforeAllTest.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.github.glytching.junit.extension.folder; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.is; + +@ExtendWith(TemporaryFolderExtension.class) +public class TemporaryFolderExtensionBeforeAllTest { + + private static TemporaryFolder TEMPORARY_FOLDER; + + @BeforeAll + public static void setUp(TemporaryFolder givenTemporaryFolder) { + TEMPORARY_FOLDER = givenTemporaryFolder; + } + + @AfterAll + public static void cleanUp() throws IOException { + try (Stream stream = Files.list(TEMPORARY_FOLDER.getRoot().toPath())) { + Set createdFileNames = + stream.map(path -> path.toFile().getName()).collect(Collectors.toSet()); + + // when using a static TemporaryFolder, every test gets the same instance so in AfterAll the + // folder should contain all artifacts created by all tests in this test case + assertThat(createdFileNames.size(), is(2)); + assertThat(createdFileNames, hasItem("foo.txt")); + assertThat(createdFileNames, hasItem("bar")); + } + } + + @Test + public void canCreateAFile() throws IOException { + File file = TEMPORARY_FOLDER.createFile("foo.txt"); + assertThat(file.exists(), is(true)); + } + + @Test + public void canCreateADirectory() throws IOException { + File file = TEMPORARY_FOLDER.createDirectory("bar"); + assertThat(file.exists(), is(true)); + } +} diff --git a/src/test/java/io/github/glytching/junit/extension/folder/TemporaryFolderExtensionFieldTest.java b/src/test/java/io/github/glytching/junit/extension/folder/TemporaryFolderExtensionBeforeEachTest.java similarity index 98% rename from src/test/java/io/github/glytching/junit/extension/folder/TemporaryFolderExtensionFieldTest.java rename to src/test/java/io/github/glytching/junit/extension/folder/TemporaryFolderExtensionBeforeEachTest.java index 91ea676..7f3b3a7 100644 --- a/src/test/java/io/github/glytching/junit/extension/folder/TemporaryFolderExtensionFieldTest.java +++ b/src/test/java/io/github/glytching/junit/extension/folder/TemporaryFolderExtensionBeforeEachTest.java @@ -35,7 +35,7 @@ import static org.hamcrest.Matchers.*; @ExtendWith(TemporaryFolderExtension.class) -public class TemporaryFolderExtensionFieldTest { +public class TemporaryFolderExtensionBeforeEachTest { // gather the temporary file and directory paths to facilitate assertions on (a) the distinct-ness // of the temporary folder address supplied to each test and (b) the removal of each temporary diff --git a/src/test/java/io/github/glytching/junit/extension/folder/TemporaryFolderExtensionMetaTest.java b/src/test/java/io/github/glytching/junit/extension/folder/TemporaryFolderExtensionMetaTest.java deleted file mode 100644 index d2e20bd..0000000 --- a/src/test/java/io/github/glytching/junit/extension/folder/TemporaryFolderExtensionMetaTest.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.github.glytching.junit.extension.folder; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.api.extension.ExtensionContext.Namespace; -import org.junit.jupiter.api.extension.ExtensionContext.Store; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.io.IOException; - -import static org.mockito.Mockito.*; - -/** - * We cannot use the normal test flow to verify all aspects of the {@link - * TemporaryFolderExtension}. Specifically: - * - *

    - * - * In addition, we cannot test these aspects using the {@link - * io.github.glytching.junit.extension.util.ExtensionTester} because that has no access to the extension's - * internals. - * - *

    So, we test this extension behaviour by playing around with the {@link - * TemporaryFolderExtension} directly. - */ -public class TemporaryFolderExtensionMetaTest { - - private final TemporaryFolderExtension sut = new TemporaryFolderExtension(); - @Mock private ExtensionContext extensionContext; - @Mock private Store store; - @Mock private TemporaryFolder temporaryFolder; - - @BeforeEach - public void prepare() { - MockitoAnnotations.initMocks(this); - - when(extensionContext.getStore( - Namespace.create(TemporaryFolderExtension.class, extensionContext))) - .thenReturn(store); - } - - @Test - void willDestroyTemporaryFolderAfterEach() throws IOException { - when(store.get(any(String.class), eq(TemporaryFolder.class))).thenReturn(temporaryFolder); - - sut.afterEach(extensionContext); - - verify(temporaryFolder).destroy(); - } - - @Test - void willSwallowAnyExceptionEncounteredWhenDestroyingTheTemporaryFolder() throws IOException { - when(store.get(any(String.class), eq(TemporaryFolder.class))).thenReturn(temporaryFolder); - - doThrow(new RuntimeException("Boom!")).when(temporaryFolder).destroy(); - - sut.afterEach(extensionContext); - } -}