diff --git a/bom/runtime/pom.xml b/bom/runtime/pom.xml
index 4336eae73f892..93c7195a009d2 100644
--- a/bom/runtime/pom.xml
+++ b/bom/runtime/pom.xml
@@ -427,6 +427,11 @@
quarkus-panache-common
${project.version}
+
+ io.quarkus
+ quarkus-panache-mock
+ ${project.version}
+
io.quarkus
quarkus-panacheql
diff --git a/docs/src/main/asciidoc/getting-started-testing.adoc b/docs/src/main/asciidoc/getting-started-testing.adoc
index a2b669527b8fb..fdaa29db155d3 100644
--- a/docs/src/main/asciidoc/getting-started-testing.adoc
+++ b/docs/src/main/asciidoc/getting-started-testing.adoc
@@ -525,6 +525,10 @@ public class MockGreetingServiceTest {
----
<1> Since we configured `greetingService` as a mock, the `GreetingResource` which uses the `GreetingService` bean, we get the mocked response instead of the response of the regular `GreetingService` bean
+=== Mocking with Panache
+
+If you are using the `quarkus-hibernate-orm-panache` or `quarkus-mongodb-panache` extensions, check out the link:hibernate-orm-panache#mocking[Hibernate ORM with Panache Mocking] and link:mongodb-panache#mocking[MongoDB with Panache Mocking] documentation for the easiest way to mock your data access.
+
== Test Bootstrap Configuration Options
There are a few system properties that can be used to tune the bootstrap of the test, specifically its classpath.
diff --git a/docs/src/main/asciidoc/hibernate-orm-panache.adoc b/docs/src/main/asciidoc/hibernate-orm-panache.adoc
index 8dcd69ff252ef..92d6492a2fab5 100644
--- a/docs/src/main/asciidoc/hibernate-orm-panache.adoc
+++ b/docs/src/main/asciidoc/hibernate-orm-panache.adoc
@@ -611,7 +611,7 @@ public void create(Parameter parameter){
Panache provides direct support for database locking with your entity/repository, using `findById(Object, LockModeType)` or `find().withLock(LockModeType)`.
-The following examples are for the entity pattern, but the same can be used with repositories.
+The following examples are for the active record pattern, but the same can be used with repositories.
=== First: Locking using findById().
@@ -686,6 +686,184 @@ public class PersonRepository implements PanacheRepositoryBase {
}
----
+== Mocking
+
+=== Using the active record pattern
+
+If you are using the active record pattern you cannot use Mockito directly as it does not support mocking static methods,
+but you can use the `quarkus-panache-mock` module which allows you to use Mockito to mock all provided static
+methods, including your own.
+
+Add this dependency to your `pom.xml`:
+
+[source,xml]
+----
+
+ io.quarkus
+ quarkus-panache-mock
+ test
+
+----
+
+
+Given this simple entity:
+
+[source,java]
+----
+@Entity
+public class Person extends PanacheEntity {
+
+ public String name;
+
+ public static List findOrdered() {
+ return find("ORDER BY name").list();
+ }
+}
+----
+
+You can write your mocking test like this:
+
+[source,java]
+----
+@QuarkusTest
+public class PanacheFunctionalityTest {
+
+ @Test
+ public void testPanacheMocking() {
+ PanacheMock.mock(Person.class);
+
+ // Mocked classes always return a default value
+ Assertions.assertEquals(0, Person.count());
+
+ // Now let's specify the return value
+ Mockito.when(Person.count()).thenReturn(23l);
+ Assertions.assertEquals(23, Person.count());
+
+ // Now let's change the return value
+ Mockito.when(Person.count()).thenReturn(42l);
+ Assertions.assertEquals(42, Person.count());
+
+ // Now let's call the original method
+ Mockito.when(Person.count()).thenCallRealMethod();
+ Assertions.assertEquals(0, Person.count());
+
+ // Check that we called it 4 times
+ PanacheMock.verify(Person.class, Mockito.times(4)).count();// <1>
+
+ // Mock only with specific parameters
+ Person p = new Person();
+ Mockito.when(Person.findById(12l)).thenReturn(p);
+ Assertions.assertSame(p, Person.findById(12l));
+ Assertions.assertNull(Person.findById(42l));
+
+ // Mock throwing
+ Mockito.when(Person.findById(12l)).thenThrow(new WebApplicationException());
+ Assertions.assertThrows(WebApplicationException.class, () -> Person.findById(12l));
+
+ // We can even mock your custom methods
+ Mockito.when(Person.findOrdered()).thenReturn(Collections.emptyList());
+ Assertions.assertTrue(Person.findOrdered().isEmpty());
+
+ PanacheMock.verify(Person.class).findOrdered();
+ PanacheMock.verify(Person.class, Mockito.atLeastOnce()).findById(Mockito.any());
+ PanacheMock.verifyNoMoreInteractions(Person.class);
+ }
+}
+----
+<1> Be sure to call your `verify` methods on `PanacheMock` rather than `Mockito`, otherwise you won't know
+what mock object to pass.
+
+=== Using the repository pattern
+
+If you are using the repository pattern you can use Mockito directly, using the `quarkus-junit5-mockito` module,
+which makes mocking beans much easier:
+
+[source,java]
+----
+
+ io.quarkus
+ quarkus-junit5-mockito
+ test
+
+----
+
+Given this simple entity:
+
+[source,java]
+----
+@Entity
+public class Person {
+
+ @Id
+ @GeneratedValue
+ public Long id;
+
+ public String name;
+}
+----
+
+And this repository:
+
+[source,java]
+----
+@ApplicationScoped
+public class PersonRepository implements PanacheRepository {
+ public List findOrdered() {
+ return find("ORDER BY name").list();
+ }
+}
+----
+
+You can write your mocking test like this:
+
+[source,java]
+----
+@QuarkusTest
+public class PanacheFunctionalityTest {
+ @InjectMock
+ PersonRepository personRepository;
+
+ @Test
+ public void testPanacheRepositoryMocking() throws Throwable {
+ // Mocked classes always return a default value
+ Assertions.assertEquals(0, personRepository.count());
+
+ // Now let's specify the return value
+ Mockito.when(personRepository.count()).thenReturn(23l);
+ Assertions.assertEquals(23, personRepository.count());
+
+ // Now let's change the return value
+ Mockito.when(personRepository.count()).thenReturn(42l);
+ Assertions.assertEquals(42, personRepository.count());
+
+ // Now let's call the original method
+ Mockito.when(personRepository.count()).thenCallRealMethod();
+ Assertions.assertEquals(0, personRepository.count());
+
+ // Check that we called it 4 times
+ Mockito.verify(personRepository, Mockito.times(4)).count();
+
+ // Mock only with specific parameters
+ Person p = new Person();
+ Mockito.when(personRepository.findById(12l)).thenReturn(p);
+ Assertions.assertSame(p, personRepository.findById(12l));
+ Assertions.assertNull(personRepository.findById(42l));
+
+ // Mock throwing
+ Mockito.when(personRepository.findById(12l)).thenThrow(new WebApplicationException());
+ Assertions.assertThrows(WebApplicationException.class, () -> personRepository.findById(12l));
+
+ Mockito.when(personRepository.findOrdered()).thenReturn(Collections.emptyList());
+ Assertions.assertTrue(personRepository.findOrdered().isEmpty());
+
+ // We can even mock your custom methods
+ Mockito.verify(personRepository).findOrdered();
+ Mockito.verify(personRepository, Mockito.atLeastOnce()).findById(Mockito.any());
+ Mockito.verifyNoMoreInteractions(personRepository);
+ }
+}
+----
+
== How and why we simplify Hibernate ORM mappings
When it comes to writing Hibernate ORM entities, there are a number of annoying things that users have grown used to
diff --git a/docs/src/main/asciidoc/mongodb-panache.adoc b/docs/src/main/asciidoc/mongodb-panache.adoc
index 474dc668c89bd..3b3f9d7add49c 100644
--- a/docs/src/main/asciidoc/mongodb-panache.adoc
+++ b/docs/src/main/asciidoc/mongodb-panache.adoc
@@ -867,6 +867,181 @@ public Multi streamPersons() {
TIP: `@SseElementType(MediaType.APPLICATION_JSON)` tells RESTEasy to serialize the object in JSON.
+== Mocking
+
+=== Using the active-record pattern
+
+If you are using the active-record pattern you cannot use Mockito directly as it does not support mocking static methods,
+but you can use the `quarkus-panache-mock` module which allows you to use Mockito to mock all provided static
+methods, including your own.
+
+Add this dependency to your `pom.xml`:
+
+[source,xml]
+----
+
+ io.quarkus
+ quarkus-panache-mock
+ test
+
+----
+
+Given this simple entity:
+
+[source,java]
+----
+public class Person extends PanacheMongoEntity {
+
+ public String name;
+
+ public static List findOrdered() {
+ return findAll(Sort.by("lastname", "firstname")).list();
+ }
+}
+----
+
+You can write your mocking test like this:
+
+[source,java]
+----
+@QuarkusTest
+public class PanacheFunctionalityTest {
+
+ @Test
+ public void testPanacheMocking() {
+ PanacheMock.mock(Person.class);
+
+ // Mocked classes always return a default value
+ Assertions.assertEquals(0, Person.count());
+
+ // Now let's specify the return value
+ Mockito.when(Person.count()).thenReturn(23l);
+ Assertions.assertEquals(23, Person.count());
+
+ // Now let's change the return value
+ Mockito.when(Person.count()).thenReturn(42l);
+ Assertions.assertEquals(42, Person.count());
+
+ // Now let's call the original method
+ Mockito.when(Person.count()).thenCallRealMethod();
+ Assertions.assertEquals(0, Person.count());
+
+ // Check that we called it 4 times
+ PanacheMock.verify(Person.class, Mockito.times(4)).count();// <1>
+
+ // Mock only with specific parameters
+ Person p = new Person();
+ Mockito.when(Person.findById(12l)).thenReturn(p);
+ Assertions.assertSame(p, Person.findById(12l));
+ Assertions.assertNull(Person.findById(42l));
+
+ // Mock throwing
+ Mockito.when(Person.findById(12l)).thenThrow(new WebApplicationException());
+ Assertions.assertThrows(WebApplicationException.class, () -> Person.findById(12l));
+
+ // We can even mock your custom methods
+ Mockito.when(Person.findOrdered()).thenReturn(Collections.emptyList());
+ Assertions.assertTrue(Person.findOrdered().isEmpty());
+
+ PanacheMock.verify(Person.class).findOrdered();
+ PanacheMock.verify(Person.class, Mockito.atLeastOnce()).findById(Mockito.any());
+ PanacheMock.verifyNoMoreInteractions(Person.class);
+ }
+}
+----
+<1> Be sure to call your `verify` methods on `PanacheMock` rather than `Mockito`, otherwise you won't know
+what mock object to pass.
+
+=== Using the repository pattern
+
+If you are using the repository pattern you can use Mockito directly, using the `quarkus-junit5-mockito` module,
+which makes mocking beans much easier:
+
+[source,java]
+----
+
+ io.quarkus
+ quarkus-junit5-mockito
+ test
+
+----
+
+Given this simple entity:
+
+[source,java]
+----
+public class Person {
+
+ @BsonId
+ public Long id;
+
+ public String name;
+}
+----
+
+And this repository:
+
+[source,java]
+----
+@ApplicationScoped
+public class PersonRepository implements PanacheMongoRepository {
+ public List findOrdered() {
+ return findAll(Sort.by("lastname", "firstname")).list();
+ }
+}
+----
+
+You can write your mocking test like this:
+
+[source,java]
+----
+@QuarkusTest
+public class PanacheFunctionalityTest {
+ @InjectMock
+ PersonRepository personRepository;
+
+ @Test
+ public void testPanacheRepositoryMocking() throws Throwable {
+ // Mocked classes always return a default value
+ Assertions.assertEquals(0, personRepository.count());
+
+ // Now let's specify the return value
+ Mockito.when(personRepository.count()).thenReturn(23l);
+ Assertions.assertEquals(23, personRepository.count());
+
+ // Now let's change the return value
+ Mockito.when(personRepository.count()).thenReturn(42l);
+ Assertions.assertEquals(42, personRepository.count());
+
+ // Now let's call the original method
+ Mockito.when(personRepository.count()).thenCallRealMethod();
+ Assertions.assertEquals(0, personRepository.count());
+
+ // Check that we called it 4 times
+ Mockito.verify(personRepository, Mockito.times(4)).count();
+
+ // Mock only with specific parameters
+ Person p = new Person();
+ Mockito.when(personRepository.findById(12l)).thenReturn(p);
+ Assertions.assertSame(p, personRepository.findById(12l));
+ Assertions.assertNull(personRepository.findById(42l));
+
+ // Mock throwing
+ Mockito.when(personRepository.findById(12l)).thenThrow(new WebApplicationException());
+ Assertions.assertThrows(WebApplicationException.class, () -> personRepository.findById(12l));
+
+ Mockito.when(personRepository.findOrdered()).thenReturn(Collections.emptyList());
+ Assertions.assertTrue(personRepository.findOrdered().isEmpty());
+
+ // We can even mock your custom methods
+ Mockito.verify(personRepository).findOrdered();
+ Mockito.verify(personRepository, Mockito.atLeastOnce()).findById(Mockito.any());
+ Mockito.verifyNoMoreInteractions(personRepository);
+ }
+}
+----
+
+
== How and why we simplify MongoDB API
When it comes to writing MongoDB entities, there are a number of annoying things that users have grown used to
diff --git a/extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheJpaEntityEnhancer.java b/extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheJpaEntityEnhancer.java
index f29c14ad090c2..7e3135519826c 100644
--- a/extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheJpaEntityEnhancer.java
+++ b/extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheJpaEntityEnhancer.java
@@ -1,6 +1,7 @@
package io.quarkus.hibernate.orm.panache.deployment;
import java.lang.reflect.Modifier;
+import java.util.List;
import javax.persistence.Transient;
@@ -22,6 +23,7 @@
import io.quarkus.panache.common.deployment.EntityModel;
import io.quarkus.panache.common.deployment.MetamodelInfo;
import io.quarkus.panache.common.deployment.PanacheEntityEnhancer;
+import io.quarkus.panache.common.deployment.PanacheMethodCustomizer;
public class PanacheJpaEntityEnhancer extends PanacheEntityEnhancer>> {
@@ -38,15 +40,15 @@ public class PanacheJpaEntityEnhancer extends PanacheEntityEnhancer methodCustomizers) {
+ super(index, PanacheResourceProcessor.DOTNAME_PANACHE_ENTITY_BASE, methodCustomizers);
modelInfo = new MetamodelInfo<>();
}
@Override
public ClassVisitor apply(String className, ClassVisitor outputClassVisitor) {
return new PanacheJpaEntityClassVisitor(className, outputClassVisitor, modelInfo, panacheEntityBaseClassInfo,
- indexView.getClassByName(DotName.createSimple(className)));
+ indexView.getClassByName(DotName.createSimple(className)), methodCustomizers);
}
static class PanacheJpaEntityClassVisitor extends PanacheEntityClassVisitor {
@@ -54,8 +56,9 @@ static class PanacheJpaEntityClassVisitor extends PanacheEntityClassVisitor> modelInfo,
ClassInfo panacheEntityBaseClassInfo,
- ClassInfo entityInfo) {
- super(className, outputClassVisitor, modelInfo, panacheEntityBaseClassInfo, entityInfo);
+ ClassInfo entityInfo,
+ List methodCustomizers) {
+ super(className, outputClassVisitor, modelInfo, panacheEntityBaseClassInfo, entityInfo, methodCustomizers);
}
@Override
diff --git a/extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheResourceProcessor.java b/extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheResourceProcessor.java
index 7de5c41758178..3d64066db93f5 100644
--- a/extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheResourceProcessor.java
+++ b/extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheResourceProcessor.java
@@ -6,6 +6,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.stream.Collectors;
import javax.persistence.EntityManager;
import javax.persistence.NamedQueries;
@@ -38,6 +39,8 @@
import io.quarkus.panache.common.deployment.MetamodelInfo;
import io.quarkus.panache.common.deployment.PanacheEntityClassesBuildItem;
import io.quarkus.panache.common.deployment.PanacheFieldAccessEnhancer;
+import io.quarkus.panache.common.deployment.PanacheMethodCustomizer;
+import io.quarkus.panache.common.deployment.PanacheMethodCustomizerBuildItem;
import io.quarkus.panache.common.deployment.PanacheRepositoryEnhancer;
public final class PanacheResourceProcessor {
@@ -76,7 +79,11 @@ void build(CombinedIndexBuildItem index,
BuildProducer transformers,
HibernateEnhancersRegisteredBuildItem hibernateMarker,
BuildProducer entityClasses,
- BuildProducer namedQueries) throws Exception {
+ BuildProducer namedQueries,
+ List methodCustomizersBuildItems) throws Exception {
+
+ List methodCustomizers = methodCustomizersBuildItems.stream()
+ .map(bi -> bi.getMethodCustomizer()).collect(Collectors.toList());
PanacheJpaRepositoryEnhancer daoEnhancer = new PanacheJpaRepositoryEnhancer(index.getIndex());
Set daoClasses = new HashSet<>();
@@ -107,7 +114,7 @@ void build(CombinedIndexBuildItem index,
namedQueries.produce(new NamedQueryEntityClassBuildStep(parameterType.name().toString(), typeNamedQueries));
}
- PanacheJpaEntityEnhancer modelEnhancer = new PanacheJpaEntityEnhancer(index.getIndex());
+ PanacheJpaEntityEnhancer modelEnhancer = new PanacheJpaEntityEnhancer(index.getIndex(), methodCustomizers);
Set modelClasses = new HashSet<>();
// Note that we do this in two passes because for some reason Jandex does not give us subtypes
// of PanacheEntity if we ask for subtypes of PanacheEntityBase
diff --git a/extensions/panache/hibernate-orm-panache/runtime/pom.xml b/extensions/panache/hibernate-orm-panache/runtime/pom.xml
index 619864a3a1296..db189f7b4f32d 100644
--- a/extensions/panache/hibernate-orm-panache/runtime/pom.xml
+++ b/extensions/panache/hibernate-orm-panache/runtime/pom.xml
@@ -39,6 +39,16 @@
quarkus-jackson
true
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
org.hibernate
diff --git a/extensions/panache/mongodb-panache/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/PanacheMongoEntityEnhancer.java b/extensions/panache/mongodb-panache/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/PanacheMongoEntityEnhancer.java
index e786ff47d73e9..45b64eb2f4ee7 100644
--- a/extensions/panache/mongodb-panache/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/PanacheMongoEntityEnhancer.java
+++ b/extensions/panache/mongodb-panache/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/PanacheMongoEntityEnhancer.java
@@ -2,6 +2,7 @@
import java.lang.reflect.Modifier;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import org.bson.codecs.pojo.annotations.BsonIgnore;
@@ -19,6 +20,7 @@
import io.quarkus.panache.common.deployment.EntityModel;
import io.quarkus.panache.common.deployment.MetamodelInfo;
import io.quarkus.panache.common.deployment.PanacheEntityEnhancer;
+import io.quarkus.panache.common.deployment.PanacheMethodCustomizer;
public class PanacheMongoEntityEnhancer extends PanacheEntityEnhancer>> {
public final static String MONGO_OPERATIONS_NAME = MongoOperations.class.getName();
@@ -28,23 +30,23 @@ public class PanacheMongoEntityEnhancer extends PanacheEntityEnhancer entities = new HashMap<>();
- public PanacheMongoEntityEnhancer(IndexView index) {
- super(index, PanacheResourceProcessor.DOTNAME_PANACHE_ENTITY_BASE);
+ public PanacheMongoEntityEnhancer(IndexView index, List methodCustomizers) {
+ super(index, PanacheResourceProcessor.DOTNAME_PANACHE_ENTITY_BASE, methodCustomizers);
modelInfo = new MetamodelInfo<>();
}
@Override
public ClassVisitor apply(String className, ClassVisitor outputClassVisitor) {
return new PanacheMongoEntityClassVisitor(className, outputClassVisitor, modelInfo, panacheEntityBaseClassInfo,
- indexView.getClassByName(DotName.createSimple(className)));
+ indexView.getClassByName(DotName.createSimple(className)), methodCustomizers);
}
static class PanacheMongoEntityClassVisitor extends PanacheEntityClassVisitor {
public PanacheMongoEntityClassVisitor(String className, ClassVisitor outputClassVisitor,
MetamodelInfo> modelInfo, ClassInfo panacheEntityBaseClassInfo,
- ClassInfo entityInfo) {
- super(className, outputClassVisitor, modelInfo, panacheEntityBaseClassInfo, entityInfo);
+ ClassInfo entityInfo, List methodCustomizers) {
+ super(className, outputClassVisitor, modelInfo, panacheEntityBaseClassInfo, entityInfo, methodCustomizers);
}
@Override
diff --git a/extensions/panache/mongodb-panache/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/PanacheResourceProcessor.java b/extensions/panache/mongodb-panache/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/PanacheResourceProcessor.java
index 96ae8cdf948f6..25bfd54ddcb43 100644
--- a/extensions/panache/mongodb-panache/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/PanacheResourceProcessor.java
+++ b/extensions/panache/mongodb-panache/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/PanacheResourceProcessor.java
@@ -7,6 +7,7 @@
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
import org.bson.codecs.pojo.annotations.BsonProperty;
import org.bson.types.ObjectId;
@@ -51,6 +52,8 @@
import io.quarkus.mongodb.panache.reactive.ReactivePanacheMongoRepositoryBase;
import io.quarkus.panache.common.deployment.PanacheEntityClassesBuildItem;
import io.quarkus.panache.common.deployment.PanacheFieldAccessEnhancer;
+import io.quarkus.panache.common.deployment.PanacheMethodCustomizer;
+import io.quarkus.panache.common.deployment.PanacheMethodCustomizerBuildItem;
import io.quarkus.panache.common.deployment.PanacheRepositoryEnhancer;
public class PanacheResourceProcessor {
@@ -133,7 +136,11 @@ void buildImperative(CombinedIndexBuildItem index,
BuildProducer transformers,
BuildProducer reflectiveClass,
BuildProducer propertyMappingClass,
- BuildProducer entityClasses) {
+ BuildProducer entityClasses,
+ List methodCustomizersBuildItems) {
+
+ List methodCustomizers = methodCustomizersBuildItems.stream()
+ .map(bi -> bi.getMethodCustomizer()).collect(Collectors.toList());
PanacheMongoRepositoryEnhancer daoEnhancer = new PanacheMongoRepositoryEnhancer(index.getIndex());
Set daoClasses = new HashSet<>();
@@ -167,7 +174,7 @@ void buildImperative(CombinedIndexBuildItem index,
propertyMappingClass.produce(new PropertyMappingClassBuildStep(parameterType.name().toString()));
}
- PanacheMongoEntityEnhancer modelEnhancer = new PanacheMongoEntityEnhancer(index.getIndex());
+ PanacheMongoEntityEnhancer modelEnhancer = new PanacheMongoEntityEnhancer(index.getIndex(), methodCustomizers);
Set modelClasses = new HashSet<>();
// Note that we do this in two passes because for some reason Jandex does not give us subtypes
// of PanacheMongoEntity if we ask for subtypes of PanacheMongoEntityBase
@@ -214,7 +221,11 @@ void buildMutiny(CombinedIndexBuildItem index,
ApplicationIndexBuildItem applicationIndex,
BuildProducer reflectiveClass,
BuildProducer propertyMappingClass,
- BuildProducer transformers) {
+ BuildProducer transformers,
+ List methodCustomizersBuildItems) {
+
+ List methodCustomizers = methodCustomizersBuildItems.stream()
+ .map(bi -> bi.getMethodCustomizer()).collect(Collectors.toList());
ReactivePanacheMongoRepositoryEnhancer daoEnhancer = new ReactivePanacheMongoRepositoryEnhancer(index.getIndex());
Set daoClasses = new HashSet<>();
@@ -249,7 +260,8 @@ void buildMutiny(CombinedIndexBuildItem index,
propertyMappingClass.produce(new PropertyMappingClassBuildStep(parameterType.name().toString()));
}
- ReactivePanacheMongoEntityEnhancer modelEnhancer = new ReactivePanacheMongoEntityEnhancer(index.getIndex());
+ ReactivePanacheMongoEntityEnhancer modelEnhancer = new ReactivePanacheMongoEntityEnhancer(index.getIndex(),
+ methodCustomizers);
Set modelClasses = new HashSet<>();
// Note that we do this in two passes because for some reason Jandex does not give us subtypes
// of PanacheMongoEntity if we ask for subtypes of PanacheMongoEntityBase
diff --git a/extensions/panache/mongodb-panache/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/ReactivePanacheMongoEntityEnhancer.java b/extensions/panache/mongodb-panache/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/ReactivePanacheMongoEntityEnhancer.java
index f204029cdd645..679d8cb9794ce 100644
--- a/extensions/panache/mongodb-panache/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/ReactivePanacheMongoEntityEnhancer.java
+++ b/extensions/panache/mongodb-panache/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/ReactivePanacheMongoEntityEnhancer.java
@@ -2,6 +2,7 @@
import java.lang.reflect.Modifier;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import org.bson.codecs.pojo.annotations.BsonIgnore;
@@ -19,6 +20,7 @@
import io.quarkus.panache.common.deployment.EntityModel;
import io.quarkus.panache.common.deployment.MetamodelInfo;
import io.quarkus.panache.common.deployment.PanacheEntityEnhancer;
+import io.quarkus.panache.common.deployment.PanacheMethodCustomizer;
public class ReactivePanacheMongoEntityEnhancer extends PanacheEntityEnhancer>> {
public final static String MONGO_OPERATIONS_NAME = ReactiveMongoOperations.class.getName();
@@ -28,23 +30,23 @@ public class ReactivePanacheMongoEntityEnhancer extends PanacheEntityEnhancer entities = new HashMap<>();
- public ReactivePanacheMongoEntityEnhancer(IndexView index) {
- super(index, PanacheResourceProcessor.DOTNAME_MUTINY_PANACHE_ENTITY_BASE);
+ public ReactivePanacheMongoEntityEnhancer(IndexView index, List methodCustomizers) {
+ super(index, PanacheResourceProcessor.DOTNAME_MUTINY_PANACHE_ENTITY_BASE, methodCustomizers);
modelInfo = new MetamodelInfo<>();
}
@Override
public ClassVisitor apply(String className, ClassVisitor outputClassVisitor) {
return new PanacheMongoEntityClassVisitor(className, outputClassVisitor, modelInfo, panacheEntityBaseClassInfo,
- indexView.getClassByName(DotName.createSimple(className)));
+ indexView.getClassByName(DotName.createSimple(className)), methodCustomizers);
}
static class PanacheMongoEntityClassVisitor extends PanacheEntityClassVisitor {
public PanacheMongoEntityClassVisitor(String className, ClassVisitor outputClassVisitor,
MetamodelInfo> modelInfo, ClassInfo panacheEntityBaseClassInfo,
- ClassInfo entityInfo) {
- super(className, outputClassVisitor, modelInfo, panacheEntityBaseClassInfo, entityInfo);
+ ClassInfo entityInfo, List methodCustomizers) {
+ super(className, outputClassVisitor, modelInfo, panacheEntityBaseClassInfo, entityInfo, methodCustomizers);
}
@Override
diff --git a/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/JandexUtil.java b/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/JandexUtil.java
index 42bab758fae6d..475e1990dc9ba 100644
--- a/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/JandexUtil.java
+++ b/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/JandexUtil.java
@@ -426,8 +426,6 @@ public static Type[] getParameterTypes(String methodDescriptor) {
}
return args.toArray(new Type[0]);
}
-<<<<<<< HEAD
-=======
public static int getParameterSize(Type paramType) {
if (paramType.kind() == Kind.PRIMITIVE) {
@@ -439,5 +437,4 @@ public static int getParameterSize(Type paramType) {
}
return 1;
}
->>>>>>> d0599c8c73... fixup enhancers
}
diff --git a/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/PanacheEntityEnhancer.java b/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/PanacheEntityEnhancer.java
index 7558c726a474b..e8f5f1bf9a1f9 100644
--- a/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/PanacheEntityEnhancer.java
+++ b/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/PanacheEntityEnhancer.java
@@ -1,5 +1,7 @@
package io.quarkus.panache.common.deployment;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -45,10 +47,13 @@ public abstract class PanacheEntityEnhancer methodCustomizers;
- public PanacheEntityEnhancer(IndexView index, DotName panacheEntityBaseName) {
+ public PanacheEntityEnhancer(IndexView index, DotName panacheEntityBaseName,
+ List methodCustomizers) {
this.panacheEntityBaseClassInfo = index.getClassByName(panacheEntityBaseName);
this.indexView = index;
+ this.methodCustomizers = methodCustomizers;
}
@Override
@@ -63,11 +68,13 @@ protected abstract static class PanacheEntityClassVisitor modelInfo;
private ClassInfo panacheEntityBaseClassInfo;
protected ClassInfo entityInfo;
+ protected List methodCustomizers;
public PanacheEntityClassVisitor(String className, ClassVisitor outputClassVisitor,
MetamodelInfo extends EntityModel extends EntityFieldType>> modelInfo,
ClassInfo panacheEntityBaseClassInfo,
- ClassInfo entityInfo) {
+ ClassInfo entityInfo,
+ List methodCustomizers) {
super(Gizmo.ASM_API_VERSION, outputClassVisitor);
thisClass = Type.getType("L" + className.replace('.', '/') + ";");
this.modelInfo = modelInfo;
@@ -75,6 +82,7 @@ public PanacheEntityClassVisitor(String className, ClassVisitor outputClassVisit
fields = entityModel != null ? entityModel.fields : null;
this.panacheEntityBaseClassInfo = panacheEntityBaseClassInfo;
this.entityInfo = entityInfo;
+ this.methodCustomizers = methodCustomizers;
}
@Override
@@ -120,6 +128,19 @@ public MethodVisitor visitMethod(int access, String methodName, String descripto
String[] exceptions) {
userMethods.add(methodName + "/" + descriptor);
MethodVisitor superVisitor = super.visitMethod(access, methodName, descriptor, signature, exceptions);
+ if (Modifier.isStatic(access)
+ && Modifier.isPublic(access)
+ && (access & Opcodes.ACC_SYNTHETIC) == 0
+ && !methodCustomizers.isEmpty()) {
+ org.jboss.jandex.Type[] argTypes = JandexUtil.getParameterTypes(descriptor);
+ MethodInfo method = this.entityInfo.method(methodName, argTypes);
+ if (method == null) {
+ throw new IllegalStateException(
+ "Could not find indexed method: " + thisClass + "." + methodName + " with descriptor " + descriptor
+ + " and arg types " + Arrays.toString(argTypes));
+ }
+ superVisitor = new PanacheMethodCustomizerVisitor(superVisitor, method, thisClass, methodCustomizers);
+ }
return new PanacheFieldAccessMethodVisitor(superVisitor, thisClass.getInternalName(), methodName, descriptor,
modelInfo);
}
@@ -162,6 +183,9 @@ private void generateMethod(MethodInfo method, AnnotationValue targetReturnTypeE
mv.visitParameter(method.parameterName(i), 0 /* modifiers */);
}
mv.visitCode();
+ for (PanacheMethodCustomizer customizer : methodCustomizers) {
+ customizer.customize(thisClass, method, mv);
+ }
// inject model
injectModel(mv);
for (int i = 0; i < parameters.size(); i++) {
diff --git a/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/PanacheMethodCustomizer.java b/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/PanacheMethodCustomizer.java
new file mode 100644
index 0000000000000..bf9a86b488bf8
--- /dev/null
+++ b/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/PanacheMethodCustomizer.java
@@ -0,0 +1,11 @@
+package io.quarkus.panache.common.deployment;
+
+import org.jboss.jandex.MethodInfo;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Type;
+
+public interface PanacheMethodCustomizer {
+
+ public void customize(Type entityClassSignature, MethodInfo method, MethodVisitor mv);
+
+}
diff --git a/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/PanacheMethodCustomizerBuildItem.java b/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/PanacheMethodCustomizerBuildItem.java
new file mode 100644
index 0000000000000..293d6cf78669a
--- /dev/null
+++ b/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/PanacheMethodCustomizerBuildItem.java
@@ -0,0 +1,18 @@
+package io.quarkus.panache.common.deployment;
+
+import io.quarkus.builder.item.MultiBuildItem;
+
+/**
+ * Build item to declare that a {@link PanacheMethodCustomizer} should be used on Panache-enhanced methods.
+ */
+public final class PanacheMethodCustomizerBuildItem extends MultiBuildItem {
+ private PanacheMethodCustomizer methodCustomizer;
+
+ public PanacheMethodCustomizerBuildItem(PanacheMethodCustomizer methodCustomizer) {
+ this.methodCustomizer = methodCustomizer;
+ }
+
+ public PanacheMethodCustomizer getMethodCustomizer() {
+ return methodCustomizer;
+ }
+}
diff --git a/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/PanacheMethodCustomizerVisitor.java b/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/PanacheMethodCustomizerVisitor.java
new file mode 100644
index 0000000000000..4fe968a6b3a53
--- /dev/null
+++ b/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/PanacheMethodCustomizerVisitor.java
@@ -0,0 +1,32 @@
+package io.quarkus.panache.common.deployment;
+
+import java.util.List;
+
+import org.jboss.jandex.MethodInfo;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Type;
+
+import io.quarkus.gizmo.Gizmo;
+
+public class PanacheMethodCustomizerVisitor extends MethodVisitor {
+
+ private List methodCustomizers;
+ private MethodInfo method;
+ private Type thisClass;
+
+ public PanacheMethodCustomizerVisitor(MethodVisitor superVisitor, MethodInfo method, Type thisClass,
+ List methodCustomizers) {
+ super(Gizmo.ASM_API_VERSION, superVisitor);
+ this.thisClass = thisClass;
+ this.method = method;
+ this.methodCustomizers = methodCustomizers;
+ }
+
+ @Override
+ public void visitCode() {
+ super.visitCode();
+ for (PanacheMethodCustomizer customizer : methodCustomizers) {
+ customizer.customize(thisClass, method, mv);
+ }
+ }
+}
diff --git a/extensions/panache/panache-mock/pom.xml b/extensions/panache/panache-mock/pom.xml
new file mode 100644
index 0000000000000..df5f35d23e27d
--- /dev/null
+++ b/extensions/panache/panache-mock/pom.xml
@@ -0,0 +1,37 @@
+
+
+
+ quarkus-build-parent
+ io.quarkus
+ 999-SNAPSHOT
+ ../../../build-parent/pom.xml
+
+ 4.0.0
+
+ quarkus-panache-mock
+ Quarkus - Panache - Mock
+ Mocking with Panache
+
+
+ io.quarkus
+ quarkus-core
+
+
+ io.quarkus
+ quarkus-arc
+
+
+ io.quarkus
+ quarkus-panache-common-deployment
+
+
+ io.quarkus
+ quarkus-junit5-mockito
+
+
+ org.mockito
+ mockito-core
+
+
+
diff --git a/extensions/panache/panache-mock/src/main/java/io/quarkus/panache/mock/PanacheMock.java b/extensions/panache/panache-mock/src/main/java/io/quarkus/panache/mock/PanacheMock.java
new file mode 100644
index 0000000000000..1c9748ef5ff27
--- /dev/null
+++ b/extensions/panache/panache-mock/src/main/java/io/quarkus/panache/mock/PanacheMock.java
@@ -0,0 +1,110 @@
+package io.quarkus.panache.mock;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.mockito.Mockito;
+import org.mockito.internal.debugging.LocationImpl;
+import org.mockito.internal.invocation.DefaultInvocationFactory;
+import org.mockito.internal.invocation.InterceptedInvocation;
+import org.mockito.internal.invocation.RealMethod;
+import org.mockito.internal.util.MockUtil;
+import org.mockito.invocation.MockHandler;
+import org.mockito.mock.MockCreationSettings;
+import org.mockito.verification.VerificationMode;
+
+public class PanacheMock {
+
+ public static volatile boolean IsMockEnabled = false;
+
+ private final static Map, Object> mocks = new ConcurrentHashMap<>();
+
+ @SuppressWarnings("unchecked")
+ public static T getMock(Class klass) {
+ return (T) mocks.get(klass);
+ }
+
+ public static Object[] getMocks(Class>... classes) {
+ Object[] mocks = new Object[classes.length];
+ for (int i = 0; i < classes.length; i++) {
+ mocks[i] = getMock(classes[i]);
+ }
+ return mocks;
+ }
+
+ public static void mock(Class>... classes) {
+ for (Class> klass : classes) {
+ mocks.computeIfAbsent(klass, v -> Mockito.mock(klass));
+ }
+ IsMockEnabled = !mocks.isEmpty();
+ }
+
+ public static void reset() {
+ mocks.clear();
+ IsMockEnabled = false;
+ }
+
+ public static boolean isMocked(Class> klass) {
+ return mocks.containsKey(klass);
+ }
+
+ public static Object mockMethod(Class> klass, String methodName, Class>[] parameterTypes, Object[] args)
+ throws InvokeRealMethodException {
+ try {
+ Method invokedMethod = klass.getDeclaredMethod(methodName, parameterTypes);
+ Object mock = getMock(klass);
+ MockCreationSettings> settings = MockUtil.getMockSettings(mock);
+ MyRealMethod myRealMethod = new MyRealMethod();
+ InterceptedInvocation invocation = DefaultInvocationFactory.createInvocation(mock, invokedMethod, args,
+ myRealMethod, settings, new LocationImpl(new Throwable(), true));
+ MockHandler> handler = MockUtil.getMockHandler(mock);
+ return handler.handle(invocation);
+ } catch (InvokeRealMethodException e) {
+ throw e;
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ //
+ // Delegating
+
+ public static T verify(Class klass) {
+ return Mockito.verify(getMock(klass));
+ }
+
+ public static T verify(Class klass, VerificationMode verificationMode) {
+ return Mockito.verify(getMock(klass), verificationMode);
+ }
+
+ public static void verifyNoMoreInteractions(Class>... classes) {
+ Mockito.verifyNoMoreInteractions(getMocks(classes));
+ }
+
+ public static void verifyNoInteractions(Class>... classes) {
+ Mockito.verifyNoInteractions(getMocks(classes));
+ }
+
+ @SuppressWarnings("serial")
+ public static class InvokeRealMethodException extends Exception {
+ }
+
+ @SuppressWarnings("serial")
+ public static class MyRealMethod implements RealMethod {
+
+ @Override
+ public boolean isInvokable() {
+ return true;
+ }
+
+ @Override
+ public Object invoke() throws Throwable {
+ throw new InvokeRealMethodException();
+ }
+
+ }
+
+}
diff --git a/extensions/panache/panache-mock/src/main/java/io/quarkus/panache/mock/impl/PanacheMockAfterEachTest.java b/extensions/panache/panache-mock/src/main/java/io/quarkus/panache/mock/impl/PanacheMockAfterEachTest.java
new file mode 100644
index 0000000000000..53a3b60cf959f
--- /dev/null
+++ b/extensions/panache/panache-mock/src/main/java/io/quarkus/panache/mock/impl/PanacheMockAfterEachTest.java
@@ -0,0 +1,13 @@
+package io.quarkus.panache.mock.impl;
+
+import io.quarkus.panache.mock.PanacheMock;
+import io.quarkus.test.junit.callback.QuarkusTestAfterEachCallback;
+
+public class PanacheMockAfterEachTest implements QuarkusTestAfterEachCallback {
+
+ @Override
+ public void afterEach(Object testInstance) {
+ PanacheMock.reset();
+ }
+
+}
diff --git a/extensions/panache/panache-mock/src/main/java/io/quarkus/panache/mock/impl/PanacheMockBuildChainCustomizer.java b/extensions/panache/panache-mock/src/main/java/io/quarkus/panache/mock/impl/PanacheMockBuildChainCustomizer.java
new file mode 100644
index 0000000000000..46334e1b6555b
--- /dev/null
+++ b/extensions/panache/panache-mock/src/main/java/io/quarkus/panache/mock/impl/PanacheMockBuildChainCustomizer.java
@@ -0,0 +1,37 @@
+package io.quarkus.panache.mock.impl;
+
+import java.util.function.Consumer;
+
+import org.jboss.jandex.Index;
+
+import io.quarkus.builder.BuildChainBuilder;
+import io.quarkus.builder.BuildContext;
+import io.quarkus.builder.BuildStep;
+import io.quarkus.deployment.builditem.LaunchModeBuildItem;
+import io.quarkus.panache.common.deployment.PanacheMethodCustomizerBuildItem;
+import io.quarkus.runtime.LaunchMode;
+import io.quarkus.test.junit.buildchain.TestBuildChainCustomizerProducer;
+
+public final class PanacheMockBuildChainCustomizer implements TestBuildChainCustomizerProducer {
+
+ @Override
+ public Consumer produce(Index testClassesIndex) {
+ return new Consumer() {
+
+ @Override
+ public void accept(BuildChainBuilder buildChainBuilder) {
+ buildChainBuilder.addBuildStep(new BuildStep() {
+ @Override
+ public void execute(BuildContext context) {
+ LaunchModeBuildItem launchMode = context.consume(LaunchModeBuildItem.class);
+ if (launchMode.getLaunchMode() == LaunchMode.TEST) {
+ context.produce(new PanacheMethodCustomizerBuildItem(new PanacheMockMethodCustomizer()));
+ }
+ }
+ }).produces(PanacheMethodCustomizerBuildItem.class)
+ .consumes(LaunchModeBuildItem.class)
+ .build();
+ }
+ };
+ }
+}
diff --git a/extensions/panache/panache-mock/src/main/java/io/quarkus/panache/mock/impl/PanacheMockMethodCustomizer.java b/extensions/panache/panache-mock/src/main/java/io/quarkus/panache/mock/impl/PanacheMockMethodCustomizer.java
new file mode 100644
index 0000000000000..00463e6a58df6
--- /dev/null
+++ b/extensions/panache/panache-mock/src/main/java/io/quarkus/panache/mock/impl/PanacheMockMethodCustomizer.java
@@ -0,0 +1,125 @@
+package io.quarkus.panache.mock.impl;
+
+import org.jboss.jandex.MethodInfo;
+import org.jboss.jandex.Type.Kind;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import io.quarkus.panache.common.deployment.JandexUtil;
+import io.quarkus.panache.common.deployment.PanacheMethodCustomizer;
+import io.quarkus.panache.mock.PanacheMock;
+
+public class PanacheMockMethodCustomizer implements PanacheMethodCustomizer {
+
+ private final static String PANACHE_MOCK_BINARY_NAME = PanacheMock.class.getName().replace('.', '/');
+ private final static String PANACHE_MOCK_INVOKE_REAL_METHOD_EXCEPTION_BINARY_NAME = PanacheMock.InvokeRealMethodException.class
+ .getName().replace('.', '/');
+
+ @Override
+ public void customize(Type entityClassSignature, MethodInfo method, MethodVisitor mv) {
+ /*
+ * Generated code:
+ *
+ * if(PanacheMock.IsMockEnabled && PanacheMock.isMocked(TestClass.class)) {
+ * try {
+ * return (int)PanacheMock.mockMethod(TestClass.class, "foo", new Class>[] {int.class}, new Object[] {arg});
+ * } catch (PanacheMock.InvokeRealMethodException e) {
+ * // fall-through
+ * }
+ * }
+ *
+ * Bytecode approx:
+ *
+ * 0: getstatic #16 // Field PanacheMock.IsMockEnabled:Z
+ * 3: ifeq 50
+ * 6: ldc #1 // class MyTestMockito$TestClass
+ * 8: invokestatic #22 // Method PanacheMock.isMocked:(Ljava/lang/Class;)Z
+ * 11: ifeq 50
+ * 14: ldc #1 // class MyTestMockito$TestClass
+ * 16: ldc #26 // String foo
+ *
+ * 18: iconst_1
+ * 19: anewarray #27 // class java/lang/Class
+ * 22: dup
+ * 23: iconst_0
+ * 24: getstatic #29 // Field java/lang/Integer.TYPE:Ljava/lang/Class;
+ * 27: aastore
+ *
+ * 28: iconst_1
+ * 29: anewarray #3 // class java/lang/Object
+ * 32: dup
+ * 33: iconst_0
+ * 34: iload_0
+ * 35: invokestatic #35 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ * 38: aastore
+ *
+ * 39: invokestatic #39 // Method
+ * PanacheMock.mockMethod:(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;[Ljava/lang/Object;)Ljava/lang/Object;
+ * 42: checkcast #30 // class java/lang/Integer
+ * 45: invokevirtual #43 // Method java/lang/Integer.intValue:()I
+ * 48: ireturn
+ * 49: astore_1
+ */
+ Label realMethodLabel = new Label();
+
+ mv.visitFieldInsn(Opcodes.GETSTATIC, PANACHE_MOCK_BINARY_NAME, "IsMockEnabled", "Z");
+ mv.visitJumpInsn(Opcodes.IFEQ, realMethodLabel);
+
+ mv.visitLdcInsn(entityClassSignature);
+ mv.visitMethodInsn(Opcodes.INVOKESTATIC, PANACHE_MOCK_BINARY_NAME, "isMocked", "(Ljava/lang/Class;)Z", false);
+ mv.visitJumpInsn(Opcodes.IFEQ, realMethodLabel);
+
+ Label tryStart = new Label();
+ Label tryEnd = new Label();
+ Label tryHandler = new Label();
+ mv.visitTryCatchBlock(tryStart, tryEnd, tryHandler, PANACHE_MOCK_INVOKE_REAL_METHOD_EXCEPTION_BINARY_NAME);
+ mv.visitLabel(tryStart);
+
+ mv.visitLdcInsn(entityClassSignature);
+ mv.visitLdcInsn(method.name());
+
+ mv.visitLdcInsn(method.parameters().size());
+ mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Class");
+
+ int i = 0;
+ for (org.jboss.jandex.Type paramType : method.parameters()) {
+ mv.visitInsn(Opcodes.DUP);
+ mv.visitLdcInsn(i);
+ JandexUtil.visitLdc(mv, paramType);
+ mv.visitInsn(Opcodes.AASTORE);
+ i++;
+ }
+
+ mv.visitLdcInsn(method.parameters().size());
+ mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
+
+ i = 0;
+ int paramSlot = 0;
+ for (org.jboss.jandex.Type paramType : method.parameters()) {
+ mv.visitInsn(Opcodes.DUP);
+ mv.visitLdcInsn(i);
+ mv.visitVarInsn(JandexUtil.getLoadOpcode(paramType), paramSlot);
+ JandexUtil.boxIfRequired(mv, paramType);
+ mv.visitInsn(Opcodes.AASTORE);
+ i++;
+ paramSlot += JandexUtil.getParameterSize(paramType);
+ }
+
+ mv.visitMethodInsn(Opcodes.INVOKESTATIC, PANACHE_MOCK_BINARY_NAME, "mockMethod",
+ "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;[Ljava/lang/Object;)Ljava/lang/Object;", false);
+ JandexUtil.unboxIfRequired(mv, method.returnType());
+ if (method.returnType().kind() != Kind.PRIMITIVE) {
+ mv.visitTypeInsn(Opcodes.CHECKCAST, method.returnType().name().toString('/'));
+ }
+
+ mv.visitInsn(JandexUtil.getReturnInstruction(method.returnType()));
+
+ mv.visitLabel(tryHandler);
+ mv.visitInsn(Opcodes.POP);
+ mv.visitLabel(tryEnd);
+
+ mv.visitLabel(realMethodLabel);
+ }
+}
diff --git a/extensions/panache/panache-mock/src/main/resources/META-INF/services/io.quarkus.test.junit.buildchain.TestBuildChainCustomizerProducer b/extensions/panache/panache-mock/src/main/resources/META-INF/services/io.quarkus.test.junit.buildchain.TestBuildChainCustomizerProducer
new file mode 100644
index 0000000000000..8e2b635300aac
--- /dev/null
+++ b/extensions/panache/panache-mock/src/main/resources/META-INF/services/io.quarkus.test.junit.buildchain.TestBuildChainCustomizerProducer
@@ -0,0 +1 @@
+io.quarkus.panache.mock.impl.PanacheMockBuildChainCustomizer
diff --git a/extensions/panache/panache-mock/src/main/resources/META-INF/services/io.quarkus.test.junit.callback.QuarkusTestAfterEachCallback b/extensions/panache/panache-mock/src/main/resources/META-INF/services/io.quarkus.test.junit.callback.QuarkusTestAfterEachCallback
new file mode 100644
index 0000000000000..fc04ccca8c2d8
--- /dev/null
+++ b/extensions/panache/panache-mock/src/main/resources/META-INF/services/io.quarkus.test.junit.callback.QuarkusTestAfterEachCallback
@@ -0,0 +1 @@
+io.quarkus.panache.mock.impl.PanacheMockAfterEachTest
diff --git a/extensions/panache/pom.xml b/extensions/panache/pom.xml
index 34220535ae042..8a36507c66f7f 100644
--- a/extensions/panache/pom.xml
+++ b/extensions/panache/pom.xml
@@ -15,6 +15,7 @@
pom
panache-common
+ panache-mock
hibernate-orm-panache
mongodb-panache
panacheql
diff --git a/integration-tests/hibernate-orm-panache/pom.xml b/integration-tests/hibernate-orm-panache/pom.xml
index 9006bfdc8c368..df67b02643d5c 100644
--- a/integration-tests/hibernate-orm-panache/pom.xml
+++ b/integration-tests/hibernate-orm-panache/pom.xml
@@ -78,6 +78,17 @@
quarkus-jackson
test
+
+ io.quarkus
+ quarkus-panache-mock
+ test
+
+
+ net.bytebuddy
+ byte-buddy
+
+
+
com.fasterxml.jackson.module
jackson-module-jaxb-annotations
diff --git a/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/MockablePersonRepository.java b/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/MockablePersonRepository.java
new file mode 100644
index 0000000000000..a83ca6dcecaa1
--- /dev/null
+++ b/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/MockablePersonRepository.java
@@ -0,0 +1,14 @@
+package io.quarkus.it.panache;
+
+import java.util.List;
+
+import javax.enterprise.context.ApplicationScoped;
+
+import io.quarkus.hibernate.orm.panache.PanacheRepository;
+
+@ApplicationScoped
+public class MockablePersonRepository implements PanacheRepository {
+ public List findOrdered() {
+ return find("ORDER BY name").list();
+ }
+}
diff --git a/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/Person.java b/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/Person.java
index 8042ebc531645..5afa058e8dbb1 100644
--- a/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/Person.java
+++ b/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/Person.java
@@ -41,7 +41,7 @@ public class Person extends PanacheEntity {
@Transient
public int serialisationTrick;
- public static List findOrdered() {
+ public static List findOrdered() {
return find("ORDER BY name").list();
}
@@ -55,4 +55,8 @@ public int getSerialisationTrick() {
public void setSerialisationTrick(int serialisationTrick) {
this.serialisationTrick = serialisationTrick;
}
+
+ public static long methodWithPrimitiveParams(boolean b, byte bb, short s, int i, long l, float f, double d, char c) {
+ return 0;
+ }
}
diff --git a/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/PersonRepository.java b/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/PersonRepository.java
index 65eaca454e303..805f66f4e3db9 100644
--- a/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/PersonRepository.java
+++ b/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/PersonRepository.java
@@ -1,9 +1,14 @@
package io.quarkus.it.panache;
+import java.util.List;
+
import javax.enterprise.context.ApplicationScoped;
import io.quarkus.hibernate.orm.panache.PanacheRepository;
@ApplicationScoped
public class PersonRepository implements PanacheRepository {
+ public List findOrdered() {
+ return find("ORDER BY name").list();
+ }
}
diff --git a/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/TestEndpoint.java b/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/TestEndpoint.java
index 3b51441e9a840..eca1f32696730 100644
--- a/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/TestEndpoint.java
+++ b/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/TestEndpoint.java
@@ -43,6 +43,10 @@
@Path("test")
public class TestEndpoint {
+ // fake unused injection point to force ArC to not remove this otherwise I can't mock it in the tests
+ @Inject
+ MockablePersonRepository mockablePersonRepository;
+
@GET
@Path("model")
@Transactional
diff --git a/integration-tests/hibernate-orm-panache/src/test/java/io/quarkus/it/panache/PanacheFunctionalityTest.java b/integration-tests/hibernate-orm-panache/src/test/java/io/quarkus/it/panache/PanacheFunctionalityTest.java
index 112ece19a4bee..add79fb5bd40f 100644
--- a/integration-tests/hibernate-orm-panache/src/test/java/io/quarkus/it/panache/PanacheFunctionalityTest.java
+++ b/integration-tests/hibernate-orm-panache/src/test/java/io/quarkus/it/panache/PanacheFunctionalityTest.java
@@ -4,12 +4,9 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.io.StringWriter;
-import java.util.Optional;
-import javax.inject.Inject;
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
-import javax.persistence.LockModeType;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
@@ -20,7 +17,6 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
-import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
import io.quarkus.test.junit.DisabledOnNativeImage;
import io.quarkus.test.junit.QuarkusTest;
import io.restassured.RestAssured;
@@ -167,35 +163,4 @@ public void testBug7721() {
public void testBug8254() {
RestAssured.when().get("/test/8254").then().body(is("OK"));
}
-
- @Inject
- PersonRepository realPersonRepository;
-
- @DisabledOnNativeImage
- @Test
- public void testPanacheRepositoryBridges() {
- // normal method call
- Assertions.assertNull(realPersonRepository.findById(0l));
- // bridge call
- Assertions.assertNull(((PanacheRepositoryBase) realPersonRepository).findById(0l));
- // normal method call
- Assertions.assertNull(realPersonRepository.findById(0l, LockModeType.NONE));
- // bridge call
- Assertions.assertNull(((PanacheRepositoryBase) realPersonRepository).findById(0l, LockModeType.NONE));
-
- // normal method call
- Assertions.assertEquals(Optional.empty(), realPersonRepository.findByIdOptional(0l));
- // bridge call
- Assertions.assertEquals(Optional.empty(), ((PanacheRepositoryBase) realPersonRepository).findByIdOptional(0l));
- // normal method call
- Assertions.assertEquals(Optional.empty(), realPersonRepository.findByIdOptional(0l, LockModeType.NONE));
- // bridge call
- Assertions.assertEquals(Optional.empty(),
- ((PanacheRepositoryBase) realPersonRepository).findByIdOptional(0l, LockModeType.NONE));
-
- // normal method call
- Assertions.assertEquals(false, realPersonRepository.deleteById(0l));
- // bridge call
- Assertions.assertEquals(false, ((PanacheRepositoryBase) realPersonRepository).deleteById(0l));
- }
}
diff --git a/integration-tests/hibernate-orm-panache/src/test/java/io/quarkus/it/panache/PanacheMockingTest.java b/integration-tests/hibernate-orm-panache/src/test/java/io/quarkus/it/panache/PanacheMockingTest.java
new file mode 100644
index 0000000000000..722070bdc989b
--- /dev/null
+++ b/integration-tests/hibernate-orm-panache/src/test/java/io/quarkus/it/panache/PanacheMockingTest.java
@@ -0,0 +1,136 @@
+package io.quarkus.it.panache;
+
+import java.util.Collections;
+import java.util.Optional;
+
+import javax.inject.Inject;
+import javax.persistence.LockModeType;
+import javax.ws.rs.WebApplicationException;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
+import io.quarkus.panache.mock.PanacheMock;
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.mockito.InjectMock;
+
+@QuarkusTest
+public class PanacheMockingTest {
+
+ @Test
+ @Order(1)
+ public void testPanacheMocking() {
+ PanacheMock.mock(Person.class);
+
+ Assertions.assertEquals(0, Person.count());
+
+ Mockito.when(Person.count()).thenReturn(23l);
+ Assertions.assertEquals(23, Person.count());
+
+ Mockito.when(Person.count()).thenReturn(42l);
+ Assertions.assertEquals(42, Person.count());
+
+ Mockito.when(Person.count()).thenCallRealMethod();
+ Assertions.assertEquals(0, Person.count());
+
+ PanacheMock.verify(Person.class, Mockito.times(4)).count();
+
+ Person p = new Person();
+ Mockito.when(Person.findById(12l)).thenReturn(p);
+ Assertions.assertSame(p, Person.findById(12l));
+ Assertions.assertNull(Person.findById(42l));
+
+ Mockito.when(Person.findById(12l)).thenThrow(new WebApplicationException());
+ try {
+ Person.findById(12l);
+ Assertions.fail();
+ } catch (WebApplicationException x) {
+ }
+
+ Mockito.when(Person.findOrdered()).thenReturn(Collections.emptyList());
+ Assertions.assertTrue(Person.findOrdered().isEmpty());
+
+ PanacheMock.verify(Person.class).findOrdered();
+ PanacheMock.verify(Person.class, Mockito.atLeastOnce()).findById(Mockito.any());
+ PanacheMock.verifyNoMoreInteractions(Person.class);
+
+ Assertions.assertEquals(0, Person.methodWithPrimitiveParams(true, (byte) 0, (short) 0, 0, 2, 2.0f, 2.0, 'c'));
+ }
+
+ @Test
+ @Order(2)
+ public void testPanacheMockingWasCleared() {
+ Assertions.assertFalse(PanacheMock.IsMockEnabled);
+ }
+
+ @InjectMock
+ MockablePersonRepository mockablePersonRepository;
+
+ @Test
+ public void testPanacheRepositoryMocking() throws Throwable {
+ Assertions.assertEquals(0, mockablePersonRepository.count());
+
+ Mockito.when(mockablePersonRepository.count()).thenReturn(23l);
+ Assertions.assertEquals(23, mockablePersonRepository.count());
+
+ Mockito.when(mockablePersonRepository.count()).thenReturn(42l);
+ Assertions.assertEquals(42, mockablePersonRepository.count());
+
+ Mockito.when(mockablePersonRepository.count()).thenCallRealMethod();
+ Assertions.assertEquals(0, mockablePersonRepository.count());
+
+ Mockito.verify(mockablePersonRepository, Mockito.times(4)).count();
+
+ Person p = new Person();
+ Mockito.when(mockablePersonRepository.findById(12l)).thenReturn(p);
+ Assertions.assertSame(p, mockablePersonRepository.findById(12l));
+ Assertions.assertNull(mockablePersonRepository.findById(42l));
+
+ Mockito.when(mockablePersonRepository.findById(12l)).thenThrow(new WebApplicationException());
+ try {
+ mockablePersonRepository.findById(12l);
+ Assertions.fail();
+ } catch (WebApplicationException x) {
+ }
+
+ Mockito.when(mockablePersonRepository.findOrdered()).thenReturn(Collections.emptyList());
+ Assertions.assertTrue(mockablePersonRepository.findOrdered().isEmpty());
+
+ Mockito.verify(mockablePersonRepository).findOrdered();
+ Mockito.verify(mockablePersonRepository, Mockito.atLeastOnce()).findById(Mockito.any());
+ Mockito.verifyNoMoreInteractions(mockablePersonRepository);
+ }
+
+ @Inject
+ PersonRepository realPersonRepository;
+
+ @Test
+ public void testPanacheRepositoryBridges() {
+ // normal method call
+ Assertions.assertNull(realPersonRepository.findById(0l));
+ // bridge call
+ Assertions.assertNull(((PanacheRepositoryBase) realPersonRepository).findById(0l));
+ // normal method call
+ Assertions.assertNull(realPersonRepository.findById(0l, LockModeType.NONE));
+ // bridge call
+ Assertions.assertNull(((PanacheRepositoryBase) realPersonRepository).findById(0l, LockModeType.NONE));
+
+ // normal method call
+ Assertions.assertEquals(Optional.empty(), realPersonRepository.findByIdOptional(0l));
+ // bridge call
+ Assertions.assertEquals(Optional.empty(), ((PanacheRepositoryBase) realPersonRepository).findByIdOptional(0l));
+ // normal method call
+ Assertions.assertEquals(Optional.empty(), realPersonRepository.findByIdOptional(0l, LockModeType.NONE));
+ // bridge call
+ Assertions.assertEquals(Optional.empty(),
+ ((PanacheRepositoryBase) realPersonRepository).findByIdOptional(0l, LockModeType.NONE));
+
+ // normal method call
+ Assertions.assertEquals(false, realPersonRepository.deleteById(0l));
+ // bridge call
+ Assertions.assertEquals(false, ((PanacheRepositoryBase) realPersonRepository).deleteById(0l));
+ }
+}
diff --git a/integration-tests/mongodb-panache/pom.xml b/integration-tests/mongodb-panache/pom.xml
index c5be2c1c9709a..fe0c950cfb7ab 100755
--- a/integration-tests/mongodb-panache/pom.xml
+++ b/integration-tests/mongodb-panache/pom.xml
@@ -44,6 +44,11 @@
quarkus-junit5
test
+
+ io.quarkus
+ quarkus-panache-mock
+ test
+
io.rest-assured
rest-assured
diff --git a/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/person/MockablePersonRepository.java b/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/person/MockablePersonRepository.java
new file mode 100644
index 0000000000000..51d645b204e97
--- /dev/null
+++ b/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/person/MockablePersonRepository.java
@@ -0,0 +1,15 @@
+package io.quarkus.it.mongodb.panache.person;
+
+import java.util.List;
+
+import javax.enterprise.context.ApplicationScoped;
+
+import io.quarkus.mongodb.panache.PanacheMongoRepositoryBase;
+import io.quarkus.panache.common.Sort;
+
+@ApplicationScoped
+public class MockablePersonRepository implements PanacheMongoRepositoryBase {
+ public List findOrdered() {
+ return findAll(Sort.by("lastname", "firstname")).list();
+ }
+}
diff --git a/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/person/PersonEntity.java b/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/person/PersonEntity.java
index 4a146d9d9c1a5..be33769b45e73 100644
--- a/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/person/PersonEntity.java
+++ b/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/person/PersonEntity.java
@@ -1,12 +1,19 @@
package io.quarkus.it.mongodb.panache.person;
+import java.util.List;
+
import org.bson.codecs.pojo.annotations.BsonId;
import io.quarkus.mongodb.panache.PanacheMongoEntityBase;
+import io.quarkus.panache.common.Sort;
public class PersonEntity extends PanacheMongoEntityBase {
@BsonId
public Long id;
public String firstname;
public String lastname;
+
+ public static List findOrdered() {
+ return findAll(Sort.by("lastname", "firstname")).list();
+ }
}
diff --git a/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/person/PersonRepositoryResource.java b/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/person/PersonRepositoryResource.java
index e84584094bc04..8c7e1a54e3741 100644
--- a/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/person/PersonRepositoryResource.java
+++ b/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/person/PersonRepositoryResource.java
@@ -15,6 +15,10 @@
@Consumes(MediaType.APPLICATION_JSON)
public class PersonRepositoryResource {
+ // fake unused injection point to force ArC to not remove this otherwise I can't mock it in the tests
+ @Inject
+ MockablePersonRepository mockablePersonRepository;
+
@Inject
PersonRepository personRepository;
diff --git a/integration-tests/mongodb-panache/src/test/java/io/quarkus/it/mongodb/panache/MongodbPanacheMockingTest.java b/integration-tests/mongodb-panache/src/test/java/io/quarkus/it/mongodb/panache/MongodbPanacheMockingTest.java
new file mode 100644
index 0000000000000..1f5d7ad5f72be
--- /dev/null
+++ b/integration-tests/mongodb-panache/src/test/java/io/quarkus/it/mongodb/panache/MongodbPanacheMockingTest.java
@@ -0,0 +1,131 @@
+package io.quarkus.it.mongodb.panache;
+
+import java.util.Collections;
+import java.util.Optional;
+
+import javax.inject.Inject;
+import javax.ws.rs.WebApplicationException;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import io.quarkus.it.mongodb.panache.person.MockablePersonRepository;
+import io.quarkus.it.mongodb.panache.person.PersonEntity;
+import io.quarkus.it.mongodb.panache.person.PersonRepository;
+import io.quarkus.mongodb.panache.PanacheMongoRepositoryBase;
+import io.quarkus.panache.mock.PanacheMock;
+import io.quarkus.test.common.QuarkusTestResource;
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.mockito.InjectMock;
+
+@QuarkusTest
+@QuarkusTestResource(MongoTestResource.class)
+public class MongodbPanacheMockingTest {
+
+ @Test
+ @Order(1)
+ public void testPanacheMocking() {
+ PanacheMock.mock(PersonEntity.class);
+
+ Assertions.assertEquals(0, PersonEntity.count());
+
+ Mockito.when(PersonEntity.count()).thenReturn(23l);
+ Assertions.assertEquals(23, PersonEntity.count());
+
+ Mockito.when(PersonEntity.count()).thenReturn(42l);
+ Assertions.assertEquals(42, PersonEntity.count());
+
+ Mockito.when(PersonEntity.count()).thenCallRealMethod();
+ Assertions.assertEquals(0, PersonEntity.count());
+
+ PanacheMock.verify(PersonEntity.class, Mockito.times(4)).count();
+
+ PersonEntity p = new PersonEntity();
+
+ Mockito.when(PersonEntity.findById(12l)).thenReturn(p);
+ Assertions.assertSame(p, PersonEntity.findById(12l));
+ Assertions.assertNull(PersonEntity.findById(42l));
+
+ Mockito.when(PersonEntity.findById(12l)).thenThrow(new WebApplicationException());
+ try {
+ PersonEntity.findById(12l);
+ Assertions.fail();
+ } catch (WebApplicationException x) {
+ }
+
+ Mockito.when(PersonEntity.findOrdered()).thenReturn(Collections.emptyList());
+ Assertions.assertTrue(PersonEntity.findOrdered().isEmpty());
+
+ PanacheMock.verify(PersonEntity.class).findOrdered();
+ PanacheMock.verify(PersonEntity.class, Mockito.atLeastOnce()).findById(Mockito.any());
+ PanacheMock.verifyNoMoreInteractions(PersonEntity.class);
+ }
+
+ @Test
+ @Order(2)
+ public void testPanacheMockingWasCleared() {
+ Assertions.assertFalse(PanacheMock.IsMockEnabled);
+ }
+
+ @InjectMock
+ MockablePersonRepository mockablePersonRepository;
+
+ @Test
+ public void testPanacheRepositoryMocking() throws Throwable {
+ Assertions.assertEquals(0, mockablePersonRepository.count());
+
+ Mockito.when(mockablePersonRepository.count()).thenReturn(23l);
+ Assertions.assertEquals(23, mockablePersonRepository.count());
+
+ Mockito.when(mockablePersonRepository.count()).thenReturn(42l);
+ Assertions.assertEquals(42, mockablePersonRepository.count());
+
+ Mockito.when(mockablePersonRepository.count()).thenCallRealMethod();
+ Assertions.assertEquals(0, mockablePersonRepository.count());
+
+ Mockito.verify(mockablePersonRepository, Mockito.times(4)).count();
+
+ PersonEntity p = new PersonEntity();
+ Mockito.when(mockablePersonRepository.findById(12l)).thenReturn(p);
+ Assertions.assertSame(p, mockablePersonRepository.findById(12l));
+ Assertions.assertNull(mockablePersonRepository.findById(42l));
+
+ Mockito.when(mockablePersonRepository.findById(12l)).thenThrow(new WebApplicationException());
+ try {
+ mockablePersonRepository.findById(12l);
+ Assertions.fail();
+ } catch (WebApplicationException x) {
+ }
+
+ Mockito.when(mockablePersonRepository.findOrdered()).thenReturn(Collections.emptyList());
+ Assertions.assertTrue(mockablePersonRepository.findOrdered().isEmpty());
+
+ Mockito.verify(mockablePersonRepository).findOrdered();
+ Mockito.verify(mockablePersonRepository, Mockito.atLeastOnce()).findById(Mockito.any());
+ Mockito.verifyNoMoreInteractions(mockablePersonRepository);
+ }
+
+ @Inject
+ PersonRepository realPersonRepository;
+
+ @Test
+ public void testPanacheRepositoryBridges() {
+ // normal method call
+ Assertions.assertNull(realPersonRepository.findById(0l));
+ // bridge call
+ Assertions.assertNull(((PanacheMongoRepositoryBase) realPersonRepository).findById(0l));
+
+ // normal method call
+ Assertions.assertEquals(Optional.empty(), realPersonRepository.findByIdOptional(0l));
+ // bridge call
+ Assertions.assertEquals(Optional.empty(), ((PanacheMongoRepositoryBase) realPersonRepository).findByIdOptional(0l));
+
+ // normal method call
+ Assertions.assertEquals(false, realPersonRepository.deleteById(0l));
+ // bridge call
+ Assertions.assertEquals(false, ((PanacheMongoRepositoryBase) realPersonRepository).deleteById(0l));
+ }
+
+}
diff --git a/integration-tests/mongodb-panache/src/test/java/io/quarkus/it/mongodb/panache/MongodbPanacheResourceTest.java b/integration-tests/mongodb-panache/src/test/java/io/quarkus/it/mongodb/panache/MongodbPanacheResourceTest.java
index a111e25f2704a..dedf714d3983f 100644
--- a/integration-tests/mongodb-panache/src/test/java/io/quarkus/it/mongodb/panache/MongodbPanacheResourceTest.java
+++ b/integration-tests/mongodb-panache/src/test/java/io/quarkus/it/mongodb/panache/MongodbPanacheResourceTest.java
@@ -10,9 +10,6 @@
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
-import java.util.Optional;
-
-import javax.inject.Inject;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -24,10 +21,7 @@
import io.quarkus.it.mongodb.panache.book.BookDetail;
import io.quarkus.it.mongodb.panache.person.Person;
-import io.quarkus.it.mongodb.panache.person.PersonRepository;
-import io.quarkus.mongodb.panache.PanacheMongoRepositoryBase;
import io.quarkus.test.common.QuarkusTestResource;
-import io.quarkus.test.junit.DisabledOnNativeImage;
import io.quarkus.test.junit.QuarkusTest;
import io.restassured.RestAssured;
import io.restassured.common.mapper.TypeRef;
@@ -360,26 +354,4 @@ public void testMoreEntityFunctionalities() {
public void testMoreRepositoryFunctionalities() {
get("/test/imperative/repository").then().statusCode(200);
}
-
- @Inject
- PersonRepository realPersonRepository;
-
- @DisabledOnNativeImage
- @Test
- public void testPanacheRepositoryBridges() {
- // normal method call
- Assertions.assertNull(realPersonRepository.findById(0l));
- // bridge call
- Assertions.assertNull(((PanacheMongoRepositoryBase) realPersonRepository).findById(0l));
-
- // normal method call
- Assertions.assertEquals(Optional.empty(), realPersonRepository.findByIdOptional(0l));
- // bridge call
- Assertions.assertEquals(Optional.empty(), ((PanacheMongoRepositoryBase) realPersonRepository).findByIdOptional(0l));
-
- // normal method call
- Assertions.assertEquals(false, realPersonRepository.deleteById(0l));
- // bridge call
- Assertions.assertEquals(false, ((PanacheMongoRepositoryBase) realPersonRepository).deleteById(0l));
- }
}