How do I ensure in a JUnit test case, that all the the threads spawned directly/indirectly by the method under test are done with there job, so that I can assert the final result?
@Test
public void testMethod() {
Result result=method();// may spawn multiple threads to set result.value
Assert.assertTrue(result.getValue()==4); //should execute only after result.value is set to its final value.
}
The real question is, how do you deal with this case in your non-test code? What does the API of method()
guarantee to its callers about when result.value
will be set?
Bear that in mind strongly when writing tests - the purpose is to assert that the class and its methods behave as they advertise. Sometimes, working out what the advertised interface is can be half of the challenge.
In a situation like this, I would strongly recommend that your Result
object behave like a Future
, in that its get()
method blocks until the result is available. (Alternatively, give it a waitFor()
method or similar).
If your method doesn't provide any specific guarantees or blocking calls, all you can really do in the test is to keep checking the value every x seconds in a loop, putting a @Timeout
on the test to ensure that the value is set in a "reasonable" time. But this is all a client would be able to do too, so
- it's a very valid test;
- it highlights that the interface isn't very usable for clients and modifying it would be a nice idea.
I suggest you invoke the Thread#join on multiple threads instances in method(),follow this way,all sub threads are completed.
One approach for dealing with this requires two steps:
- Block the main test thread while waiting for worker threads
- Have each worker thread signal the main thread when its work is completed, unblocking the main thread when the last worker completes.
Ideally, you'll want the main thread to timeout and fail the test if all of the workers do not complete within the expected time frame. This is pretty straightforward using ConcurrentUnit:
public class MyTest extends ConcurrentTestCase {
@Test
public void test() throws Throwable {
int threads = 5;
for (int i = 0; i < threads; i++) {
new Thread(new Runnable() {
public void run() {
threadAssertEquals(1, 1); // Perform assertions as needed
resume();
}
}).start();
}
threadWait(1000, threads); // Wait for 5 resume calls
}
}