Restassured Testing Reactive Quarkus blocking in IO thread

Viewed 110

I have a quarkus application that I am trying to test using Restassured I am however getting this error when testing:

[org.jbo.res.rea.ser.cor.RuntimeExceptionMapper] (vert.x-eventloop-thread-1) A blocking operation occurred on the IO thread. This likely means you need to annotate my.package.MyController#update(class my.package.MyRequestDto) with @io.smallrye.common.annotation.Blocking. Alternatively you can annotate the class my.package.MyController to make every method on the class blocking, or annotate your sub class of the javax.ws.rs.core.Application class to make the whole application blocking

If i run this application and test manually using postman this does not seem to happen so it is a matter of test setup that seems to be the issue.

My test is as follows:

@QuarkusTest
public class MyTest {
  @Test
  @TestReactiveTransaction
  public void test_A() {
              Response response = given()
                      .when()
                      .accept(MediaTypes.JSON_TYPE)
                      .contentType(MediaTypes.JSON_TYPE)
                      .body("{myRequestJsonHere}")
                      .patch("/path/to/api")
                      .then()
                      .extract().response();
      // asserts

  }
}

Inside my controller:

@Path("/path/to/api")
@ApplicationScoped
public class MyController{
  @Inject MyService service;

  @PATCH
  public Uni<MyResultType> update(@NotNull @RequestBody @Valid MyRequestDto request) {
     return service.update(request);
  }
}

My service:

@ApplicationScoped
public class MyService {
  @Inject MyRepository repository;

  @Transactional
  public Uni<MyResultType> update(MyRequestDto request) {
    return repository.findAll().firstResult()
       .onItem().transform(l -> mapping here))
       .flatMap(repository::persistAndFlush);
  }
}

My repository:

public interface MyRepository extends PanacheRepository<MyResultType> {
}

I have also tried wrapping the full test content in a UniAsserter.execute(() -> { }); but still got the same issue.

Is there some more setup I need to do to ensure the thread is used properly?

2 Answers

@Transactional doesn't work with Reactive Panache or Hibernate Reactive. You can use @ReactiveTransactional or Panache.withTransaction.

You shouldn't use @TestReactiveTransactional for the test. You are simulating a Rest call, there's no reason to open a transaction. I think what's happening is that you are starting a reactive transaction and then RestAssure will call the REST API and block the client until the result is received.I suspect this is what's causing the issue.

You can change the code of the test to:

@QuarkusTest
public class MyTest {

  @Test
  public void test_A() {
              Response response = given()
                      .when()
                      .accept(MediaTypes.JSON_TYPE)
                      .contentType(MediaTypes.JSON_TYPE)
                      .body("{myRequestJsonHere}")
                      .patch("/path/to/api")
                      .then()
                      .extract().response();
  }
}

The service becomes:

@ApplicationScoped
public class MyService {
  @Inject MyRepository repository;

  @ReactiveTransactional
  public Uni<MyResultType> update(MyRequestDto request) {
    return repository
       .findAll()
       .firstResult()
       .map(l -> /* update the entity... */ );
  }
}

The persist should only be necessary if you are creating a new entity. You can add it back if you need it.

I believe that as you're doing one database access, your thread should be defined as blocking.

As your error message says, you can include one @Blocking annotation on your method or your class.

Quarkus guide about blocking threads on WRITING REST SERVICES WITH RESTEASY REACTIVE

I guess your class could be like this:

import io.smallrye.common.annotation.Blocking;

@Path("/path/to/api")
@ApplicationScoped
public class MyController{
  @Inject MyService service;

  @Blocking
  @PATCH
  public Uni<MyResultType> update(@NotNull @RequestBody @Valid MyRequestDto request) {
     return service.update(request);
  }
}
Related