-
Notifications
You must be signed in to change notification settings - Fork 2.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Can not mock non-static persist method on PanacheEntityBase #17697
Comments
/cc @FroMage, @loicmathieu |
To mock instance methods of an entity you need to actually mock the session: @QuarkusTest
public class PanacheMockingTest {
@InjectMock
Mutiny.Session session;
@Test
public void testPanacheSessionMocking() {
Person p = new Person();
// mocked via Mutiny.Session mocking
p.persist().await().indefinitely();
Assertions.assertNull(p.id);
Mockito.verify(session, Mockito.times(1)).persist(Mockito.any());
}
} |
Where in the example though do I set up an interaction? In Panache reactive, the I want to set up an interaction that is something like "When the Mocking the |
Here, the issue is that you normally should create a mocked Fruit with Mockito but you cannot as the Fruit object is created by the resteasy serialization layer. I'm afrad there is no solution to what you want to achieve, if you test your application at the HTTP layer using restassured, you must use the restassured assertion (at the http layer). Here I would remove all the code related to mockito/panachemock. |
Also adding @InjectMock
Mutiny.Session session; doesn't work. This is what I get in the output: [ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 6.667 s <<< FAILURE! - in org.acme.rest.FruitResourceTests
[ERROR] org.acme.rest.FruitResourceTests.addFruit Time elapsed: 0.004 s <<< ERROR!
org.junit.jupiter.api.extension.TestInstantiationException: Failed to create test instance
Caused by: java.lang.reflect.InvocationTargetException
Caused by: org.mockito.exceptions.base.MockitoException:
Mockito cannot mock this class: class io.quarkus.hibernate.reactive.runtime.ReactiveSessionProducer_ProducerMethod_createMutinySession_1321d110ee9e92bda147899150401e0a136779c7_ClientProxy.
Mockito can only mock non-private & non-final classes.
If you're not sure why you're getting this error, please report to the mailing list.
Java : 11
JVM vendor name : Oracle Corporation
JVM vendor version : 11.0.2+9
JVM name : OpenJDK 64-Bit Server VM
JVM version : 11.0.2+9
JVM info : mixed mode
OS name : Mac OS X
OS version : 10.16
Underlying exception : java.lang.IllegalArgumentException: Unknown type: null
Caused by: java.lang.IllegalArgumentException: Unknown type: null
2021-06-07 10:04:49,594 INFO [io.quarkus] (main) Quarkus stopped in 0.033s
[INFO]
[INFO] Results:
[INFO]
[ERROR] Errors:
[ERROR] FruitResourceTests.addFruit » TestInstantiation Failed to create test instance
[INFO]
[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0
[INFO] |
I can use the rest-assured at the HTTP layer to validate the returned json is correct, but I don't need/want an entire persistence back-end just to verify my HTTP layer. I can accomplish this just fine using the repository pattern. For the active record pattern, I'll continue with what I was doing before: @POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Uni<Fruit> addFruit(@Valid Fruit fruit) {
return Panache.withTransaction(() ->
Fruit.persist(fruit)
.replaceWith(fruit)
);
} and the tests @Test
public void addFruit() {
PanacheMock.mock(Fruit.class);
Mockito.when(Fruit.persist(Mockito.any(Fruit.class), Mockito.any()))
.thenReturn(Uni.createFrom().voidItem());
given()
.when()
.contentType(ContentType.JSON)
.body("{\"id\":1,\"name\":\"Grapefruit\",\"description\":\"Summer fruit\"}")
.post("/fruits")
.then()
.statusCode(200)
.contentType(ContentType.JSON)
.body(
"id", is(1),
"name", is("Grapefruit"),
"description", is("Summer fruit")
);
PanacheMock.verify(Fruit.class).persist(Mockito.any(Fruit.class), Mockito.any());
PanacheMock.verifyNoMoreInteractions(Fruit.class);
} |
@edeandrea yes, this is the difference between repository where you can easily mock it, and entity. Again, here, the Fruit is created by the serialization layer and there is no way you mock the instanciation of an object JVM wide! The new is made by the serialization layer, so with entity I recommend using embedded database (h2 or testcontainer based database). |
Thank you @loicmathieu & @FroMage for the conversation! |
I have a fix for that in #17728 it's a bug. |
Awesome! Scanning that ticket I see
Would that be an annotation I could put on a method instead of having to do something like @POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Uni<Fruit> addFruit(@Valid Fruit fruit) {
return Panache.withTransaction(() -> this.fruitRepository.persist(fruit));
} ? |
Yes. |
Nice! Why not reuse the current |
Because |
Got it - thanks @FroMage ! Is this going to be part of Quarkus 2.0? Or a later 2.x version? Will |
Hopefully 2.0 yes. I don't know what |
https://quarkus.io/guides/getting-started-testing#tests-and-transactions @QuarkusTest
@TestTransaction
class FruitRepositoryTests {
@Inject
FruitRepository fruitRepository;
@Test
public void findByName() {
this.fruitRepository.persist(new Fruit(null, "Grapefruit", "Summer fruit"));
Optional<Fruit> fruit = this.fruitRepository.findByName("Grapefruit");
assertThat(fruit)
.isNotNull()
.isPresent()
.get()
.extracting(Fruit::getName, Fruit::getDescription)
.containsExactly("Grapefruit", "Summer fruit");
assertThat(fruit.get().getId())
.isNotNull()
.isGreaterThan(2L);
}
} whereas currently in Panache reactive I have to do something like @QuarkusTest
class FruitRepositoryTests {
@Inject
FruitRepository fruitRepository;
@Test
public void findByName() {
Fruit fruit = Panache.getSession().withTransaction(tx -> {
tx.markForRollback();
return this.fruitRepository
.persist(new Fruit(null, "Grapefruit", "Summer fruit"))
.replaceWith(this.fruitRepository.findByName("Grapefruit"));
})
.await()
.atMost(Duration.ofSeconds(10));
assertThat(fruit)
.isNotNull()
.extracting(Fruit::getName, Fruit::getDescription)
.containsExactly("Grapefruit", "Summer fruit");
assertThat(fruit.getId())
.isNotNull()
.isGreaterThan(2L);
}
} |
Describe the bug
In Quarkus
2.0.0.CR3
(an most likely in1.13.x
) using Panache reactive I am not able to mock the non-static methods onPanacheEntityBase
(persist()
for example - I haven't tried the others). Mockito does not allow you to mock them because Mockito requires an instance inMockito.when
/Mockito.doReturn(..).when
..PanacheMock
does not seem to work either.To Reproduce
I've attached a reproducer project:
panache-reactive-activerecord-persist-mock.zip
src/main/java/org/acme/rest/FruitResource.java
src/test/java/org/acme/rest/FruitResourceTests.java
Running
./mvnw clean verify
produces the following test failure, indicating thePanacheMock.doReturn(Uni.createFrom().item(new Fruit(100L, "Grapefruit", "Summer fruit"))).when(Fruit.class).persist();
is not actually being "picked up". The assertedid
should be100
, but it isn't.Environment (please complete the following information):
Output of
uname -a
orver
Darwin edeandre-mac 20.4.0 Darwin Kernel Version 20.4.0: Thu Apr 22 21:46:47 PDT 2021; root:xnu-7195.101.2~1/RELEASE_X86_64 x86_64
Output of
java -version
openjdk version "11.0.2" 2019-01-15 OpenJDK Runtime Environment 18.9 (build 11.0.2+9) OpenJDK 64-Bit Server VM 18.9 (build 11.0.2+9, mixed mode)
GraalVM version (if different from Java)
N/A
Quarkus version or git rev
2.0.0.CR3
Build tool (ie. output of
mvnw --version
orgradlew --version
)The text was updated successfully, but these errors were encountered: