16 April 2013

Unit testing potential thread leakage

So you've bewildered yourself into the dark world of pain threads, and you wanna make sure your app doesn't leak threads.

I found I had to calm my nerves by having tonnes of these Spock tests covering my Thread spawning.

I took my actual code, pasted it into this blog, stripped and rewrote it to cut straight to the point.

This silly service only illustrates the two methods for starting and stopping threads. This sample only spawns 1 thread but the point here are tests below.
import java.util.*
// very silly example spawning a thread
class MyService {
TimerTask task
Timer timer
def startJobs() {
timer = new Timer("Job") //Setting job name is important for the test
task = timer.runAfter(10000, {})
}
def endJobs() {
timer.cancel()
task.cancel()
}
}


To be sure your startJobs actually creates your thread(s), you should have a test confirming that first.
void "starting jobs creates threads"() {
when:
myService.startJobs()
Thread.sleep(300)
then:
Thread.getAllStackTraces().keySet().findAll \\
{Thread thread -> thread.name.contains "Job"}.size() > 1
cleanup:
myService.endJobs() //important, or you risk threads contaminating your next test. (happened to me)
}


And here is the test looking for leakage:
void "end jobs should leave no threads"() {
setup:
myService.startJobs()
when:
myService.endJobs()
Thread.sleep(300)
then:
Thread.getAllStackTraces().keySet().findAll \\
{Thread thread -> thread.name.contains "Job"}.size() == 0
}


The whole idea is finding the threads by your given thread name. And yes, i know Thread.sleep isn't ideal, and I may not need it, but it just feels reasonable to make sure the thread gets a little time to get going.

Side note: I learned that you have to cancel both the Timer and the TimerTask. If you only cancel the Timer, you'll have to wait for the GC to clean up the thread, and your test will fail.

No comments:

Post a Comment