JUnit 5 の Temporary Directory サポート


はじめに

ファイルを利用したテストを行う際に、ファイルのクリーンアップを行うのは面倒な作業です。

インメモリでファイルシステムを模倣するJimfs を利用することも出来ますが、JUnit 5 では @TempDir により、この作業を軽減できます。


JUnit 5 における一時ディレクトリサポート

JUnit 4 では、以下のように @RuleTemporaryFolder を使っていました。

 public static class HasTempFolder {
  @Rule
  public TemporaryFolder folder= new TemporaryFolder();

  @Test
  public void testUsingTempFolder() throws IOException {
      File createdFile= folder.newFile("myfile.txt");
      // ...
     }
 } 

JUnit 5 では、@TempDir アノテーションにより以下のように一時ディレクトリを利用できます。

@Test
void writeItemsToFile(@TempDir Path tempDir) throws IOException {
    Path file = tempDir.resolve("test.txt");
    // ...
}

JUnit 5 における一時ディレクトリサポートは、JUnit 5.4 で EXPERIMENTAL(実験的)機能として追加され、JUnit 5.10 にて STABLE(安定版) になりました。

  • JUnit 5.4.0  2019/02/07
    • @TempDir support for temporary directories
    • @API(status = EXPERIMENTAL, since = "5.4")
  • JUnit 5.8.0 2021/09/12
    • @TempDir can be used to create multiple temporary directories
  • JUnit 5.9.0 2022/07/26
    • Configurable cleanup mode for @TempDir
  • JUnit 5.10.0 2023/07/23
    • New TempDirFactory SPI for customizing how temporary directories are created
    • @API(status = STABLE, since = "5.10")

JUnit 5 における一時ディレクトリサポートは、拡張機能として提供されていますが、デフォルトでこの拡張機能が組み込まれているため、@ExtendWith による登録作業は不要です。


@TempDir の使い方

メソッドパラメータに @TempDir を付けることで、一時ディレクトリを得ることができ、この一時ディレクトリは、テスト終了時に破棄されます。

@Test void writeItemsToFile(@TempDir Path tempDir) throws Exception {
    var file = tempDir.resolve("test.txt");
    var lines = List.of("a", "b");
    Files.write(file, lines);
    assertLinesMatch(lines, Files.readAllLines(file));
}

@TempDir は、java.nio.file.Path または java.io.File に付与することができます。

以下のように複数の一時ディレクトリを扱うこともできます。

@Test void writeItemsToFile(@TempDir Path tempDir1,
                            @TempDir Path tempDir2) throws Exception {
}

テストクラスのインスタンスフィールドで一時ディレクトリを定義することもできます。この場合でも、複数のテストメソッドでは、それぞれ別の一時ディレクトリとなります(@BeforeAll@BeforeEach などのライフサイクルメソッドで初期設定を行い、テストメソッドで利用することができます)。

class SharedTempDirectory {
    @TempDir Path tempDir;
}

すべてのテストメソッドで同じ一時ディレクトリを使う場合は、static フィールドに @TempDir でアノテートします。

class SharedTempDirectory {

    @TempDir static Path sharedTempDir;

    @Test void test1() throws Exception { }
    @Test void test2() throws Exception { }

}


一時ディレクトリのクリーンアップ

一時ディレクトリは、テスト完了時にクリーンアップされます。

この挙動は、@TempDir アノテーションの cleanup 属性で変更できます(5.10 の段階では未だ EXPERIMENTAL)。

@Test
void fileTest(@TempDir(cleanup = ON_SUCCESS) Path tempDir) {
    // ...
}

cleanup 属性は以下があります。

public enum CleanupMode {  
    DEFAULT,   // junit.jupiter.tempdir.cleanup.mode.default の定義を利用
    ALWAYS,    // 常にクリーンアップ
    ON_SUCCESS,// テスト成功時にクリーンアップ
    NEVER;     // クリーンアップなし
}

指定しない場合は、CleanupMode.DEFAULT となり、junit.jupiter.tempdir.cleanup.mode.default で定義されたクリーンアップモードを使うようになっています(この設定は、CleanupMode.ALWAYS が設定されています)。


一時ディレクトリ生成のカスタマイズ

一時ディレクトリの生成は TempDirFactory を介して行われます。

public interface TempDirFactory extends Closeable {

    Path createTempDirectory(AnnotatedElementContext elementContext,
        ExtensionContext extensionContext) throws Exception;

    @Override
    default void close() throws IOException {
    }

デフォルトでは、以下のような TempDirFactory.Standard により一時ディレクトリが作成されます。

class Standard implements TempDirFactory {

    public static final TempDirFactory INSTANCE = new Standard();
    private static final String TEMP_DIR_PREFIX = "junit";

    @Override
    public Path createTempDirectory(AnnotatedElementContext elementContext,
            ExtensionContext extensionContext) throws IOException {
        return Files.createTempDirectory(TEMP_DIR_PREFIX);
    }
}

つまり、Java の java.io.tmpdir プロパティのディレクトリ内に、junit というプレフィックス付きで一時ディレクトリが作成されます。


この挙動は、@TempDir アノテーションの factory 属性にファクトリを指定することで変更できます(5.10 の段階では未だ EXPERIMENTAL)(設定 junit.jupiter.tempdir.factory.default により設定することも可能)。

@Test void fileTest(@TempDir(factory = MyFactory.class) Path tempDir) {
    // ...
}

指定するファクトリは、TempDirFactory を実装して以下のように実装できます。

static class MyFactory implements TempDirFactory {
    @Override
    public Path createTempDirectory(AnnotatedElementContext elementContext,
            ExtensionContext extensionContext) throws IOException {
        return Files.createTempDirectory(
                    extensionContext.getRequiredTestMethod().getName());
    }
}

この例では、一時ディレクトリのプレフィックスにテスト名を付与したものです。


まとめ

JUnit 5.10 で安定版となった @TempDir による一時ディレクトリサポートについて説明しました。

  • メソッドパラメータに @TempDir を付けることで、一時ディレクトリを得る
  • @TempDircleanup 属性で、一時ディレクトリのクリーンアップの振る舞いをカスタマイズ
  • @TempDirfactory 属性で、一時ディレクトリの生成をカスタマイズ