From 67d8c2317a247a8811c4e3799da538448480066a Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Tue, 27 Jun 2023 12:21:42 +0200 Subject: [PATCH] Introduce quarkus-test-hibernate-reactive-panache module - makes it possible to inject the TransactionalUniAsserter test method parameter --- bom/application/pom.xml | 5 +++ .../asciidoc/hibernate-reactive-panache.adoc | 37 ++++++----------- .../hibernate-reactive-panache/pom.xml | 2 +- .../TransactionalUniAsserterTest.java | 33 +++++++++++++++ .../hibernate-reactive-panache/pom.xml | 35 ++++++++++++++++ .../panache/TransactionalUniAsserter.java | 26 ++++++++++++ ...sactionalUniAsserterTestMethodInvoker.java | 40 +++++++++++++++++++ .../io.quarkus.test.TestMethodInvoker | 1 + test-framework/pom.xml | 1 + .../test/vertx/DefaultUniAsserter.java | 2 +- .../RunOnVertxContextTestMethodInvoker.java | 16 +++++++- 11 files changed, 170 insertions(+), 28 deletions(-) create mode 100644 integration-tests/hibernate-reactive-panache/src/test/java/io/quarkus/it/panache/reactive/TransactionalUniAsserterTest.java create mode 100644 test-framework/hibernate-reactive-panache/pom.xml create mode 100644 test-framework/hibernate-reactive-panache/src/main/java/io/quarkus/test/hibernate/reactive/panache/TransactionalUniAsserter.java create mode 100644 test-framework/hibernate-reactive-panache/src/main/java/io/quarkus/test/hibernate/reactive/panache/TransactionalUniAsserterTestMethodInvoker.java create mode 100644 test-framework/hibernate-reactive-panache/src/main/resources/META-INF/services/io.quarkus.test.TestMethodInvoker diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 298dea4478b0d..610a4bdb678a3 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -2997,6 +2997,11 @@ quarkus-test-vertx ${project.version} + + io.quarkus + quarkus-test-hibernate-reactive-panache + ${project.version} + io.quarkus quarkus-arquillian diff --git a/docs/src/main/asciidoc/hibernate-reactive-panache.adoc b/docs/src/main/asciidoc/hibernate-reactive-panache.adoc index e2088abc4c868..2c5a1a35e9f22 100644 --- a/docs/src/main/asciidoc/hibernate-reactive-panache.adoc +++ b/docs/src/main/asciidoc/hibernate-reactive-panache.adoc @@ -855,45 +855,34 @@ public class PersonRepository implements PanacheRepositoryBase { Testing reactive Panache entities in a `@QuarkusTest` is slightly more complicated than testing regular Panache entities due to the asynchronous nature of the APIs and the fact that all operations need to run on a Vert.x event loop. -The `quarkus-test-vertx` dependency provides the `@io.quarkus.test.vertx.RunOnVertxContext` annotation and the `io.quarkus.test.vertx.UniAsserter` class which are intended precisely for this purpose. +The `quarkus-test-vertx` dependency provides the `@io.quarkus.test.vertx.RunOnVertxContext` annotation and the `io.quarkus.test.vertx.UniAsserter` class which are intended precisely for this purpose. The usage is described in the xref:hibernate-reactive.adoc#testing[Hibernate Reactive] guide. -You can also extend the `io.quarkus.test.vertx.UniAsserterInterceptor` to wrap the injected `UniAsserter` and customize the behavior. -For example, the interceptor can be used to execute the assert methods within a separate database transaction. +Moreover, the `quarkus-test-hibernate-reactive-panache` dependency provides the `io.quarkus.test.hibernate.reactive.panache.TransactionalUniAsserter` that can be injected as a method parameter of a test method annotated with `@RunOnVertxContext`. +The `TransactionalUniAsserter` is a `io.quarkus.test.vertx.UniAsserterInterceptor` that wraps each assert method within a separate reactive transaction. -.`UniAsserterInterceptor` Example +.`TransactionalUniAsserter` Example [source,java] ---- -import io.quarkus.test.vertx.UniAsserterInterceptor; +import io.quarkus.test.hibernate.reactive.panache.TransactionalUniAsserter; @QuarkusTest public class SomeTest { - static class TransactionalUniAsserterInterceptor extends UniAsserterInterceptor { - - public TransactionUniAsserterInterceptor(UniAsserter asserter) { - super(asserter); - } - - @Override - protected Supplier> transformUni(Supplier> uniSupplier) { - // Assert/execute methods are invoked within a database transaction - return () -> Panache.withTransaction(uniSupplier); - } - } - @Test @RunOnVertxContext - public void testEntity(UniAsserter asserter) { - asserter = new TransactionalUniAsserterInterceptor(asserter); <1> - asserter.execute(() -> new MyEntity().persist()); - asserter.assertEquals(() -> MyEntity.count(), 1l); - asserter.execute(() -> MyEntity.deleteAll()); + public void testEntity(TransactionalUniAsserter asserter) { + asserter.execute(() -> new MyEntity().persist()); <1> + asserter.assertEquals(() -> MyEntity.count(), 1l); <2> + asserter.execute(() -> MyEntity.deleteAll()); <3> } } ---- -<1> The `TransactionalUniAsserterInterceptor` wraps the injected `UniAsserter`. +<1> The first reactive transaction is used to persist the entity. +<2> The second reactive transaction is used to count the entities. +<3> The third reactive transaction is used to delete all entities. +Of course, you can also define a custom `UniAsserterInterceptor` to wrap the injected `UniAsserter` and customize the behavior. == Mocking diff --git a/integration-tests/hibernate-reactive-panache/pom.xml b/integration-tests/hibernate-reactive-panache/pom.xml index 1378497593446..46ad016917eb9 100644 --- a/integration-tests/hibernate-reactive-panache/pom.xml +++ b/integration-tests/hibernate-reactive-panache/pom.xml @@ -64,7 +64,7 @@ io.quarkus - quarkus-test-vertx + quarkus-test-hibernate-reactive-panache test diff --git a/integration-tests/hibernate-reactive-panache/src/test/java/io/quarkus/it/panache/reactive/TransactionalUniAsserterTest.java b/integration-tests/hibernate-reactive-panache/src/test/java/io/quarkus/it/panache/reactive/TransactionalUniAsserterTest.java new file mode 100644 index 0000000000000..4f6abb0de637b --- /dev/null +++ b/integration-tests/hibernate-reactive-panache/src/test/java/io/quarkus/it/panache/reactive/TransactionalUniAsserterTest.java @@ -0,0 +1,33 @@ +package io.quarkus.it.panache.reactive; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.jupiter.api.Test; + +import io.quarkus.hibernate.reactive.panache.Panache; +import io.quarkus.test.hibernate.reactive.panache.TransactionalUniAsserter; +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.vertx.RunOnVertxContext; + +@QuarkusTest +public class TransactionalUniAsserterTest { + + @RunOnVertxContext + @Test + public void testTransactionalUniAsserter(TransactionalUniAsserter asserter) { + assertNotNull(asserter); + asserter.assertThat(() -> Panache.currentTransaction(), transaction -> { + assertNotNull(transaction); + assertFalse(transaction.isMarkedForRollback()); + asserter.putData("tx", transaction); + }); + asserter.assertThat(() -> Panache.currentTransaction(), transaction -> { + assertNotNull(transaction); + assertFalse(transaction.isMarkedForRollback()); + assertNotEquals(transaction, asserter.getData("tx")); + }); + } + +} diff --git a/test-framework/hibernate-reactive-panache/pom.xml b/test-framework/hibernate-reactive-panache/pom.xml new file mode 100644 index 0000000000000..7f2c5346465c8 --- /dev/null +++ b/test-framework/hibernate-reactive-panache/pom.xml @@ -0,0 +1,35 @@ + + + 4.0.0 + + + io.quarkus + quarkus-test-framework + 999-SNAPSHOT + + + quarkus-test-hibernate-reactive-panache + Quarkus - Test framework - Hibernate Reactive Panache + + + + io.quarkus + quarkus-hibernate-reactive-panache-common + + + io.quarkus + quarkus-test-vertx + + + io.quarkus + quarkus-vertx + + + io.smallrye.reactive + mutiny + + + + diff --git a/test-framework/hibernate-reactive-panache/src/main/java/io/quarkus/test/hibernate/reactive/panache/TransactionalUniAsserter.java b/test-framework/hibernate-reactive-panache/src/main/java/io/quarkus/test/hibernate/reactive/panache/TransactionalUniAsserter.java new file mode 100644 index 0000000000000..c93868235de1d --- /dev/null +++ b/test-framework/hibernate-reactive-panache/src/main/java/io/quarkus/test/hibernate/reactive/panache/TransactionalUniAsserter.java @@ -0,0 +1,26 @@ +package io.quarkus.test.hibernate.reactive.panache; + +import java.util.function.Supplier; + +import io.quarkus.hibernate.reactive.panache.common.runtime.SessionOperations; +import io.quarkus.test.vertx.UniAsserter; +import io.quarkus.test.vertx.UniAsserterInterceptor; +import io.smallrye.mutiny.Uni; + +/** + * A {@link UniAsserterInterceptor} that wraps each assert method within a separate reactive transaction. + * + * @see UniAsserter + */ +public final class TransactionalUniAsserter extends UniAsserterInterceptor { + + TransactionalUniAsserter(UniAsserter asserter) { + super(asserter); + } + + @Override + protected Supplier> transformUni(Supplier> uniSupplier) { + return () -> SessionOperations.withTransaction(() -> uniSupplier.get()); + } + +} \ No newline at end of file diff --git a/test-framework/hibernate-reactive-panache/src/main/java/io/quarkus/test/hibernate/reactive/panache/TransactionalUniAsserterTestMethodInvoker.java b/test-framework/hibernate-reactive-panache/src/main/java/io/quarkus/test/hibernate/reactive/panache/TransactionalUniAsserterTestMethodInvoker.java new file mode 100644 index 0000000000000..0ff7c21d95427 --- /dev/null +++ b/test-framework/hibernate-reactive-panache/src/main/java/io/quarkus/test/hibernate/reactive/panache/TransactionalUniAsserterTestMethodInvoker.java @@ -0,0 +1,40 @@ +package io.quarkus.test.hibernate.reactive.panache; + +import java.lang.reflect.Method; + +import io.quarkus.test.vertx.DefaultUniAsserter; +import io.quarkus.test.vertx.RunOnVertxContextTestMethodInvoker; +import io.quarkus.test.vertx.UniAsserter; + +public class TransactionalUniAsserterTestMethodInvoker extends RunOnVertxContextTestMethodInvoker { + + private UniAsserter uniAsserter; + + @Override + public boolean handlesMethodParamType(String paramClassName) { + return TransactionalUniAsserter.class.getName().equals(paramClassName); + } + + @Override + public Object methodParamInstance(String paramClassName) { + if (!handlesMethodParamType(paramClassName)) { + throw new IllegalStateException( + "TransactionalUniAsserterTestMethodInvoker does not handle '" + paramClassName + "' method param types"); + } + uniAsserter = new TransactionalUniAsserter(new DefaultUniAsserter()); + return uniAsserter; + } + + @Override + public boolean supportsMethod(Class originalTestClass, Method originalTestMethod) { + return hasSupportedAnnotation(originalTestClass, originalTestMethod) + && hasSupportedParams(originalTestMethod); + } + + private boolean hasSupportedParams(Method originalTestMethod) { + return originalTestMethod.getParameterCount() == 1 + // we need to use the class name to avoid ClassLoader issues + && originalTestMethod.getParameterTypes()[0].getName().equals(TransactionalUniAsserter.class.getName()); + } + +} diff --git a/test-framework/hibernate-reactive-panache/src/main/resources/META-INF/services/io.quarkus.test.TestMethodInvoker b/test-framework/hibernate-reactive-panache/src/main/resources/META-INF/services/io.quarkus.test.TestMethodInvoker new file mode 100644 index 0000000000000..6065c3f35a3ab --- /dev/null +++ b/test-framework/hibernate-reactive-panache/src/main/resources/META-INF/services/io.quarkus.test.TestMethodInvoker @@ -0,0 +1 @@ +io.quarkus.test.hibernate.reactive.panache.TransactionalUniAsserterTestMethodInvoker \ No newline at end of file diff --git a/test-framework/pom.xml b/test-framework/pom.xml index 3b5c3309b122a..07dbb8447101f 100644 --- a/test-framework/pom.xml +++ b/test-framework/pom.xml @@ -32,6 +32,7 @@ junit5-mockito junit5-mockito-config vertx + hibernate-reactive-panache amazon-lambda arquillian devmode-test-utils diff --git a/test-framework/vertx/src/main/java/io/quarkus/test/vertx/DefaultUniAsserter.java b/test-framework/vertx/src/main/java/io/quarkus/test/vertx/DefaultUniAsserter.java index 94d22af37c58a..43397c98bda64 100644 --- a/test-framework/vertx/src/main/java/io/quarkus/test/vertx/DefaultUniAsserter.java +++ b/test-framework/vertx/src/main/java/io/quarkus/test/vertx/DefaultUniAsserter.java @@ -10,7 +10,7 @@ import io.smallrye.mutiny.Uni; -class DefaultUniAsserter implements UniAsserter { +public final class DefaultUniAsserter implements UniAsserter { private final ConcurrentMap data; diff --git a/test-framework/vertx/src/main/java/io/quarkus/test/vertx/RunOnVertxContextTestMethodInvoker.java b/test-framework/vertx/src/main/java/io/quarkus/test/vertx/RunOnVertxContextTestMethodInvoker.java index 30c027f212135..12400237b804a 100644 --- a/test-framework/vertx/src/main/java/io/quarkus/test/vertx/RunOnVertxContextTestMethodInvoker.java +++ b/test-framework/vertx/src/main/java/io/quarkus/test/vertx/RunOnVertxContextTestMethodInvoker.java @@ -40,6 +40,18 @@ public Object methodParamInstance(String paramClassName) { @Override public boolean supportsMethod(Class originalTestClass, Method originalTestMethod) { + return hasSupportedAnnotation(originalTestClass, originalTestMethod) + && hasSupportedParams(originalTestMethod); + } + + private boolean hasSupportedParams(Method originalTestMethod) { + return originalTestMethod.getParameterCount() == 0 + || (originalTestMethod.getParameterCount() == 1 + // we need to use the class name to avoid ClassLoader issues + && originalTestMethod.getParameterTypes()[0].getName().equals(UniAsserter.class.getName())); + } + + protected boolean hasSupportedAnnotation(Class originalTestClass, Method originalTestMethod) { return hasAnnotation(RunOnVertxContext.class, originalTestMethod.getAnnotations()) || hasAnnotation(RunOnVertxContext.class, originalTestClass.getAnnotations()) || hasAnnotation(TestReactiveTransaction.class, originalTestMethod.getAnnotations()) @@ -47,7 +59,7 @@ public boolean supportsMethod(Class originalTestClass, Method originalTestMet } // we need to use the class name to avoid ClassLoader issues - private boolean hasAnnotation(Class annotation, Annotation[] annotations) { + protected boolean hasAnnotation(Class annotation, Annotation[] annotations) { return hasAnnotation(annotation.getName(), annotations); } @@ -89,7 +101,7 @@ public Object invoke(Object actualTestInstance, Method actualTestMethod, List) context.executeBlocking(handler).toCompletionStage()); } try {