javamultithreadingunit-testingexecutorservicejava-threads

Can I wait for an method and its internal executor/threads to finish before making an assertion on the test?


I have a class that basically has this code:

public class ManagerClass {
  private final ExecutorService executor = Executors.newSingleThreadExecutor();

  void methodIWantToTest(/* params */){
    try {
      executor.execute(() -> {
        privateMethodINeedToWaitFor(/* params */);
        // Other stuff
      })
    } // catch
  }
}

I need to test this method (methodIWantToTest), and at the moment the assertion runs before privateMethodINeedToWaitFor has finished. I don't know enough about threading to know if there's any way for me to wait for methodIWantToTest and every thread created within that method. The privateMethodIHaveToWaitFor updates some values that I need to check in my assertion.

I'm not comfortable refactoring this code yet as I'm not familiar enough with the codebase, so I would rather apply a solution on the test for the time being.

Currently on my test I do

managerClass.methodIWantToTest(/* params */).
Thread.sleep(1000);
List<Things> list = managerClass.getListOfThings(); 
assertEquals(1, list.size()); 
// more assertions

which works as a way to check my test passes in my local environment, the sleep() seems enough. However I'd like my code to wait for the actual threads to be done, not just have an arbitrary sleep time.


Solution

  • You can use the awaitility library, which allows for constructs like

    await()
     .atMost(30, SECONDS)
     .until(() -> managerClass.getListOfThings().size() == 1);
    

    It will check the condition every 500 ms (adjustable default) and fail after the given at most time if not fulfilled.

    One might argue it's syntactic sugar for something like

    try {
     Instant latestEndTime = Instant.now().plusSeconds(30);
     boolean ok = managerClass.getListOfThings().size() == 1;
     while (!ok) {
      if (Instant.now().isAfter(latestEndTime)) {
       fail("condition not fulfilled after 30 seconds");
      }
      Thread.sleep(500);
      ok = managerClass.getListOfThings().size() == 1;
     }
    } catch (InterruptedException x) {  // Thread.sleep() throws this
     fail("uh-oh - " + x);
    }