Skip to content

Commit

Permalink
Added panche-mock module and docs
Browse files Browse the repository at this point in the history
  • Loading branch information
FroMage committed Apr 15, 2020
1 parent 9802fce commit 7c66ef9
Show file tree
Hide file tree
Showing 36 changed files with 1,169 additions and 91 deletions.
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 @@ -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.
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

0 comments on commit 7c66ef9

Please sign in to comment.