Don’t Clean Up After Tests with RunListener.testRunFinished(), Add a Shutdown Hook Instead
Recently, while I was writing some tests which require a lot of temporary files, I ran into a problem: my tests never cleaned up the temporary files.
$ mvn test
…
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
Creating temp file for all tests: /tmp/example-junit-test-1057360590374847337.txt
[INFO] Running example.junit.ExampleTest1
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.071 s - in example.junit.ExampleTest1
[INFO] Running example.junit.ExampleTest3
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0 s - in example.junit.ExampleTest3
[INFO] Running example.junit.ExampleTest2
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0 s - in example.junit.ExampleTest2
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
…
$ ls /tmp/example-junit-test-*.txt
…
/tmp/example-junit-test-1057360590374847337.txt
I needed to clean up the temporary files after all the tests had run. Since each test needed to reside in a separate Java class, I could not rely on an @AfterClass method for clean up. So I added a RunListener.testRunFinished() method to delete the files:
@Override
public void testRunFinished(Result result) throws Exception {
// Clean up temp files.
}
This worked correctly, and the temp files were deleted after all the tests ran.
$ mvn test
…
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
Creating temp file for all tests: /tmp/example-junit-test-6810591342693798561.txt
[INFO] Running example.junit.ExampleTest1
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.065 s - in example.junit.ExampleTest1
[INFO] Running example.junit.ExampleTest3
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0 s - in example.junit.ExampleTest3
[INFO] Running example.junit.ExampleTest2
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0 s - in example.junit.ExampleTest2
Deleting temp file for all tests: /tmp/example-junit-test-6810591342693798561.txt
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
…
$ ls /tmp/example-junit-test-*.txt
…
ls: cannot access /tmp/example-junit-test-*.txt: No such file or directory
But then I ran into a different problem: when I killed the tests early with [Ctrl] + [C], the files were never deleted.
$ mvn test
…
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
Creating temp file for all tests: /tmp/example-junit-test-5067607655485865565.txt
[INFO] Running example.junit.ExampleTest1
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.058 s - in example.junit.ExampleTest1
[INFO] Running example.junit.ExampleTest3
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0 s - in example.junit.ExampleTest3
[INFO] Running example.junit.ExampleTest2
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0 s - in example.junit.ExampleTest2
^C
…
$ ls /tmp/example-junit-test-*.txt
…
/tmp/example-junit-test-5067607655485865565.txt
The solution was simple: instead of using RunListener.testRunFinished(), I needed to add a shutdown hook:
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
// Clean up temp files.
}));
Now the shutdown hook would ensure the temporary files were deleted whether the build process was killed prematurely or not!
$ mvn test
…
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
Creating temp file for all tests: /tmp/example-junit-test-2853721297857519700.txt
[INFO] Running example.junit.ExampleTest1
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.059 s - in example.junit.ExampleTest1
[INFO] Running example.junit.ExampleTest3
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.001 s - in example.junit.ExampleTest3
[INFO] Running example.junit.ExampleTest2
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0 s - in example.junit.ExampleTest2
^C
…
$ ls /tmp/example-junit-test-*.txt
…
ls: cannot access /tmp/example-junit-test-*.txt: No such file or directory
Unless you must take action based on the result of all the tests, don’t use RunListener.testRunFinished(). Instead use Runtime.addShutdownHook() to ensure that your clean up occurs even if the build/test process is killed prematurely.