Избегайте тестирования с параллельными потоками, когда это возможно (что происходит чаще всего). Это только сделает ваши тесты ненадежными (иногда пройденными, иногда неудачными).
Только когда вам нужно вызвать какую-то другую библиотеку / систему, вам, возможно, придется подождать в других потоках, в этом случае всегда используйте библиотеку Awaitility вместо Thread.sleep()
.
Никогда просто не звоните get()
или join()
в ваших тестах, иначе ваши тесты могут работать вечно на вашем CI-сервере, если будущее никогда не завершится. Всегда устанавливайте isDone()
первым в ваших тестах, прежде чем вызывать get()
. Для CompletionStage это .toCompletableFuture().isDone()
.
Когда вы тестируете неблокирующий метод, подобный этому:
public static CompletionStage<String> createGreeting(CompletableFuture<String> future) {
return future.thenApply(result -> "Hello " + result);
}
тогда вам не нужно просто проверять результат, передав завершенное Future в тесте, вы также должны убедиться, что ваш метод doSomething()
не блокируется, вызывая join()
или get()
. Это особенно важно, если вы используете неблокирующую инфраструктуру.
Чтобы сделать это, выполните тестирование с незавершенным будущим, которое вы установили как завершенное вручную:
@Test
public void testDoSomething() throws Exception {
CompletableFuture<String> innerFuture = new CompletableFuture<>();
CompletableFuture<String> futureResult = createGreeting(innerFuture).toCompletableFuture();
assertFalse(futureResult.isDone());
// this triggers the future to complete
innerFuture.complete("world");
assertTrue(futureResult.isDone());
// futher asserts about fooResult here
assertEquals(futureResult.get(), "Hello world");
}
Таким образом, если вы добавите future.join()
в doSomething (), тест не пройдёт.
Если ваша служба использует ExecutorService, например, в thenApplyAsync(..., executorService)
, то в ваших тестах внедряется однопотоковая служба ExecutorService, например, из guava:
ExecutorService executorService = Executors.newSingleThreadExecutor();
Если в вашем коде используется forkJoinPool, например thenApplyAsync(...)
, перепишите код для использования ExecutorService (есть много веских причин) или используйте Awaitility.
Чтобы сократить пример, я сделал BarService аргументом метода, реализованным как лямбда-код Java8 в тесте, обычно это была бы вставленная ссылка, которую вы бы высмеяли.