Simeon Yachev
Simeon Yachev

Reputation: 173

"AssertionError: Async not started" when trying to test a controller which invokes an @Async method

I have a similar problem to this one: when trying to make an integration test on a controller which invokes an @Async method from a service, an AssertionError: Async not started occurs.

The guy suggests something that I don't clearly understand:

you have to wrap your response Callable or DeferredResult

That's my service method:

@Async
@Override
public Future<String> migrateMovies() {
    Collection<MetadataItem> metadataMovies = metadataItemService.getAllByMetadataType(MOVIE_TYPE);

    Collection<Movie> movies = mapMovies(metadataMovies);
    movieService.saveMovies(movies);
    return CompletableFuture.supplyAsync(() -> movies.size() + " movies migrated successfully!");
}

That's my controller method:

@PostMapping("/movies")
public ResponseEntity<String> migrateMovies() throws ExecutionException, InterruptedException {
    return ResponseEntity.ok(movieMigrationService.migrateMovies().get());
}

And that's how I'm trying to test it:

@Test
@WithMockUser(username = "username")
void migrateMoviesSuccessfully() throws Exception {
    Collection<Movie> expectedMovies = Lists.list(new Movie(), new Movie());
    when(movieService.saveMovies(anyCollection())).thenReturn(expectedMovies);

    Future<String> expectedResponse =
            CompletableFuture.supplyAsync(() -> expectedMovies.size() + " movies migrated successfully!");
    when(movieMigrationService.migrateMovies()).thenReturn(expectedResponse);

    MvcResult mvcResult = mockMvc.perform(post("/movies"))
            .andExpect(request().asyncStarted())
            .andReturn();

    mockMvc.perform(asyncDispatch(mvcResult))
            .andExpect(status().isOk())
            .andExpect(content().string("2 movies migrated successfully!"));
}

Could someone explain concretely for my example how this wrapping should happen?

I'm open to any other suggestions too!

NOTE: My application is properly running asynchronously + my unit tests for the service migrateMovies() method are successful, it's only the integration test that is being stubborn :)

Upvotes: 0

Views: 4648

Answers (2)

Ebraheem Alrabeea
Ebraheem Alrabeea

Reputation: 2250

I want to add a note to the OP answer that if the request mapping is wrong or the API does not exist, the same error will appear.

Upvotes: 3

Simeon Yachev
Simeon Yachev

Reputation: 173

I managed to solve my problem quite quickly after posting. I literally just wrapped the return type of my controller in a Callable + used a supplier for the return statement:

@PostMapping("/movies")
public Callable<ResponseEntity<String>> migrateMovies() throws ExecutionException, InterruptedException {
    return () -> ResponseEntity.ok(movieMigrationService.migrateMovies().get());
}

No changes to the test!

Hope this can be helpful for somebody!

Upvotes: 2

Related Questions