Waiting for all threads spawned by my code under t

2019-06-05 15:43发布

问题:

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

回答1:

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

  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.


回答2:

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



回答3:

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