Search code examples
quarkusrest-assuredquarkus-panachequarkus-reactive

Restassured Testing Reactive Quarkus blocking in IO thread


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?


Solution

  • @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.