Skip to content
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

New panache-mock module for mocking Panache static methods #8334

Merged
merged 2 commits into from
Apr 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions bom/runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,11 @@
<artifactId>quarkus-panache-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-panache-mock</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-panacheql</artifactId>
Expand Down
4 changes: 4 additions & 0 deletions docs/src/main/asciidoc/getting-started-testing.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,10 @@ public class GreetingResourceTest {
----
<1> Indicate that this injection point is meant to use an instance of `RestClient`.

=== 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.
Expand Down
180 changes: 179 additions & 1 deletion docs/src/main/asciidoc/hibernate-orm-panache.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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().

Expand Down Expand Up @@ -686,6 +686,184 @@ public class PersonRepository implements PanacheRepositoryBase<Person,Integer> {
}
----

== 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]
----
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-panache-mock</artifactId>
<scope>test</scope>
</dependency>
----


Given this simple entity:

[source,java]
----
@Entity
public class Person extends PanacheEntity {

public String name;

public static List<Person> 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]
----
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5-mockito</artifactId>
<scope>test</scope>
</dependency>
----

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<Person> {
public List<Person> 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
Expand Down
175 changes: 175 additions & 0 deletions docs/src/main/asciidoc/mongodb-panache.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,181 @@ public Multi<ReactivePerson> 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]
----
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-panache-mock</artifactId>
<scope>test</scope>
</dependency>
----

Given this simple entity:

[source,java]
----
public class Person extends PanacheMongoEntity {

public String name;

public static List<Person> 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]
----
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5-mockito</artifactId>
<scope>test</scope>
</dependency>
----

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<Person> {
public List<Person> 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
Expand Down
Loading