argius note

プログラミング関連

JUnitでテスト後にディレクトリを自動削除する機能を使う(Rules)

比較的新しいJUnitでは、TemporaryFolder(org.junit.rules.TemporaryFolder)というRuleクラスが使用できます。
テストで使った一時ディレクトリを、テストが終わった後に自動で消すことができます。


Rulesの機能は、2009-08-04にリリースされたJUnit4.7で追加されています。


Javaで一時ディレクトリを使うには

JUnitとは関係なしに、Javaで一時ディレクトリを使う場合は、システムプロパティ"java.io.tmpdir"を参照します。
この値は、Windowsなら通常は環境変数"TMP"と同じ値が設定されています。


また、File.createTempFileメソッドを使えば、この一時ディレクトリに自動で一時ファイルを作ることができます。

File tmpfile = File.createTempFile("myproduct", null);
// => 一時ディレクトリにmyproduct1234567890123456789.tmpが作られる
//   (数字は重複しないランダム値)


この一時ディレクトリにファイルを作っても、(少なくともWindowsでは)終了時に自動で消えることはありません。Windowsなら、「ディスク クリーンアップ」機能を使う(一時ファイルを選択する)と消すことができます。


JVMが終了した時にファイルを消すFile#deleteOnExitメソッドを使えば、自動で消すことも可能です。
ファイルがごく少量で独立したものであれば、これを指定しておけばOKでしょう。


ところが、例えば、一時ディレクトリにサブディレクトリを作って、それを終了時に削除したいような場合には、話は変わってきます。
上の方法を組み合わせれば自前で出来ないこともないかも知れませんが、汎用性や信頼性を考えると、自前は避けたいところです。


Rulesについて

@Ruleアノテーションを使うと、テストフレームワーク中にAOPっぽく処理を追加することができます。
共通の前後処理をさせるなどの場合に、Rulesの実装クラスをテストケースのフィールドに@Ruleを付けて宣言すれば、いちいち@Beforeと@Afterに処理を書く必要が無くなります。


TemporaryFolder

TemporaryFolderクラス(org.junit.rules.TemporaryFolder)を使えば、テストケースが開始した時点(@Beforeのタイミング)で一時ディレクトリにサブディレクトリを作り、テストケースが終了した時点(@Afterのタイミング)でまとめて削除してくれます。当然、一部のファイルがロックされていたりすれば削除に失敗します。

public class FooTest {

    @Rule
    public TemporaryFolder tmpFolder = new TemporaryFolder();

    @Test
    public void testBar() throws IOException {
        File tmpdir = tmpFolder.newFolder("aaa");
        // 一時ディレクトリにjunit1234567890123456789\aaaが作られ、(数字は重複しないランダム値)
        // 終了時にjunit1234567890123456789ごと削除される
    }

}

注意点として、フィールドはpublicで宣言し、テストケースのインスタンス生成時にオブジェクトが生成される(コンストラクタでnewしても良い)ようにしておく必要があります。


まとめ

ファイルを大量に作ったりディレクトリ階層が複雑な出力を行う機能のユニットテストは、テストをするたびにゴミが残ってしまうのが難点です。
デスクトップ環境ならゴミ箱に放り込んだりすれば良かったりしますね。でも、うっかり消し忘れて、あとで消していいものかどうかを忘れてしまったりして...さらに、サーバで自動実行とかだとそれもできませんし。
今回の方法だと、JVMが異常終了するなどは例外として、ゴミを残すこと無く安心して好きなだけテストを実行できます。プラットフォームが変わっても問題なしですね。


Eclipseを始め、ほとんどのJavaIDEのそこそこ新しいバージョンならJUnit4.7以降が搭載されているはずですので、事実上のコアライブラリとして利用できるのがうれしいです。