Search code examples
quarkus-panachequarkus-reactivehibernate-reactivequarkus-hibernate-reactive

Quarkus Reactive/Hibernate Reactive test passes everytime, even when it should fail


I have a simple reactive CRUD application made with Quarkus while following these two articles: Getting started reactive and Hibernate reactive panache

I am trying to test my service methods, but before that I'm trying to see how the example test works. This is the example test from the article:

import io.quarkus.test.hibernate.reactive.panache.TransactionalUniAsserter;

@QuarkusTest
public class SomeTest {

    @Test
    @RunOnVertxContext
    public void testEntity(TransactionalUniAsserter asserter) {
        asserter.execute(() -> new MyEntity().persist()); 
        asserter.assertEquals(() -> MyEntity.count(), 1l); 
        asserter.execute(() -> MyEntity.deleteAll()); 
    }
}

When I try to replicate the test like below, the test always passes no matter what I set the expected count to.

Here is my test file with the test case that is green.

`@QuarkusTest
@RunOnVertxContext
class AuthorServiceTest {

    @Inject
    AuthorService service;

    @Test
    @RunOnVertxContext
    public void testGetAllAuthors(TransactionalUniAsserter a) {
        a.execute(() -> new Author("J.K.", "Rowling", LocalDate.of(1965, 7, 31)).persist());
        a.assertEquals(Author::count, 3L);
        a.execute(Author::deleteAll);
    }
}`

I am also trying to find out how to test service methods that return Uni types. I can't find any resources online that show this. Most of the ways to test seem to throw No current Vertx context found.

Here is how my model looks like:

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@Entity
@Table(name = "authors")
public class Author extends PanacheEntity {

    @Column(nullable = false)
    private String name;
    @Column(nullable = false)
    private String surname;
    @Column(nullable = false)
    private LocalDate dateOfBirth;

}

Here is my service:

import io.quarkus.hibernate.reactive.panache.Panache;
import io.quarkus.panache.common.Sort;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.ws.rs.core.Response;
import um.feri.model.Author;

import java.util.List;

@ApplicationScoped
public class AuthorService {

    public Uni<List<Author>> getAllAuthors() {
        return Author.listAll(Sort.by("surname"));
    }

    public Uni<Response> getAuthorById(Long id) {
        return Author.findById(id)
                .onItem().ifNotNull().transform(author -> Response.ok(author).build())
                .onItem().ifNull().continueWith(() -> Response.status(Response.Status.NOT_FOUND).build());
    }

    public Uni<Author> findIfAuthorExists(Author author) {
        return Author.find("name = ?1 and surname = ?2 and dateOfBirth = ?3",
                           author.getName(), author.getSurname(), author.getDateOfBirth())
                .firstResult();
    }

    public Uni<Author> addAuthor(Author author) {
        return findIfAuthorExists(author)
                .onItem().ifNotNull().transform(existingAuthor -> existingAuthor)
                .onItem().ifNull().switchTo(() -> Panache.withTransaction(author::persist).replaceWith(author));
    }

    public Uni<Response> updateAuthor(Long id, Author updatedAuthor) {
        return Author.<Author>findById(id)
                .onItem().ifNotNull().transformToUni(existingAuthor -> {
                    existingAuthor.setName(updatedAuthor.getName());
                    existingAuthor.setSurname(updatedAuthor.getSurname());
                    existingAuthor.setDateOfBirth(updatedAuthor.getDateOfBirth());

                    return Panache.withTransaction(existingAuthor::persist)
                            .replaceWith(Response.ok(existingAuthor).build());
                })
                .onItem().ifNull().continueWith(Response.status(Response.Status.NOT_FOUND).build());
    }

    public Uni<Response> deleteAuthor(Long id) {
        return Panache.withTransaction(() -> Author.deleteById(id))
                .map(deleted -> deleted
                        ? Response.ok().status(Response.Status.NO_CONTENT).build()
                        : Response.ok().status(Response.Status.NOT_FOUND).build());
    }

}

Is it better to just test the resource and leave the service methods alone? The code for this project is available on my Github

I tried using this solution but every test passes, even if I compare 2 to 3;


Solution

  • I've changed a couple of things and now the test seems to work:

    1. Upgrade Quarkus to 3.14.2. In the pom.xml:

          <properties>
           ...
           <quarkus.platform.version>3.14.2</quarkus.platform.version>
           ...
       </properties>
      
       <dependencies>
           <dependency>
               <groupId>io.quarkus</groupId>
               <artifactId>quarkus-hibernate-reactive-panache</artifactId>
           </dependency>
           <dependency>
               <groupId>io.quarkus</groupId>
               <artifactId>quarkus-rest</artifactId>
           </dependency>
           <dependency>
               <groupId>io.quarkus</groupId>
               <artifactId>quarkus-reactive-pg-client</artifactId>
           </dependency>
           <dependency>
               <groupId>io.quarkus</groupId>
               <artifactId>quarkus-rest-jackson</artifactId>
           </dependency>
           <dependency>
               <groupId>io.quarkus</groupId>
               <artifactId>quarkus-junit5</artifactId>
               <scope>test</scope>
           </dependency>
           <dependency>
               <groupId>io.rest-assured</groupId>
               <artifactId>rest-assured</artifactId>
               <scope>test</scope>
           </dependency>
           <dependency>
               <groupId>org.projectlombok</groupId>
               <artifactId>lombok</artifactId>
               <version>1.18.34</version>
               <scope>provided</scope>
           </dependency>
           <dependency>
               <groupId>io.quarkus</groupId>
               <artifactId>quarkus-test-vertx</artifactId>
               <scope>test</scope>
           </dependency>
           <dependency>
               <groupId>io.quarkus</groupId>
               <artifactId>quarkus-test-hibernate-reactive-panache</artifactId>
               <scope>test</scope>
           </dependency>
       </dependencies>
       ...
      
    2. Remove quarkus-reactive/src/test/java/um/feri/TransactionalUniAsserterInterceptor.java and update the test to:

      package um.feri.services;
      import java.time.LocalDate;
      
      import org.junit.jupiter.api.Test;
      
      import io.quarkus.test.hibernate.reactive.panache.TransactionalUniAsserter;
      import io.quarkus.test.junit.QuarkusTest;
      import io.quarkus.test.vertx.RunOnVertxContext;
      import jakarta.inject.Inject;
      import um.feri.model.Author;
      
      @QuarkusTest
      class AuthorServiceTest {
      
          @Test
          @RunOnVertxContext
          public void testGetAllAuthors(TransactionalUniAsserter a) {
               a.execute( () -> new Author( "J.K.", "Rowling", LocalDate.of( 1965, 7, 31 ) ).persist() );
      
               // Panache doesn't support  method reference
               a.assertEquals( () -> Author.count(), 6L );
               a.execute( () -> Author.deleteAll() );
          }
      }
      
    3. Optional, add quarkus.hibernate-orm.log.sql=true in the application.properties. It will show you the SQL getting executed.

    You should be able to test services the same way, for example:

        @Inject
        AuthorService service;
    
        @Test
        @RunOnVertxContext
        @Order( 1 )
        public void testGetAllAuthors(TransactionalUniAsserter a) {
            a.execute( () -> new Author( "J.K.", "Rowling", LocalDate.of( 1965, 7, 31 ) ).persist() );
            a.assertEquals( () -> Author.count(), 6L );
        }
    
        @Test
        @RunOnVertxContext
        @Order( 2 )
        public void testService(TransactionalUniAsserter a) {
            a.assertThat( () -> service.getAllAuthors(), authors -> {
                Assertions.assertNotNull( authors );
                Assertions.assertEquals( 6, authors.size() );
            } );
        }