views:

56

answers:

3

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.
}
+2  A: 

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 spceific 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 an @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

  1. it's a very valid test;
  2. it highlights that the interface isn't very usable for clients and modifying it would be a nice idea.
Andrzej Doyle
+1 your explanation is way better than mine :-)
Péter Török
+1  A: 

I suggest you invoke the Thread#join on multiple threads instances in method(),follow this way,all sub threads are completed.

Mercy
A: 

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
    }
}
Jonathan