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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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