Java Testing Tip #2:


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.

Comments

Related Posts

Java Testing Tip #1:

Programming Terms Cheat Sheet

Technical Writing

Version Control

Encapsulating Functionality