Skip to content

Commit

Permalink
4.x: Mocking documentation (#8787)
Browse files Browse the repository at this point in the history
* Mock documentation
* Helidon mock documentation

---------

Signed-off-by: tvallin <[email protected]>
  • Loading branch information
tvallin authored Jun 5, 2024
1 parent 5ba18fb commit abba8b5
Show file tree
Hide file tree
Showing 9 changed files with 262 additions and 9 deletions.
4 changes: 4 additions & 0 deletions docs/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@
<type>pom</type>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</dependency>
<!-- snippets dependencies -->
<dependency>
<groupId>org.apache.activemq</groupId>
Expand Down
2 changes: 1 addition & 1 deletion docs/src/main/asciidoc/mp/guides/testing-junit5.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ This guide demonstrated how to create tests for MicroProfile applications in a J
Refer to the following references for additional information:
* https://junit.org/junit5/docs/current/user-guide/[JUnit 5 User Guide]
* xref:../testing.adoc[Testing with JUnit 5]
* xref:../testing/testing.adoc[Testing with JUnit 5]
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
:description: Helidon Testing with TestNG
:keywords: helidon, mp, test, testing, testng
:feature-name: Testing with TestNG
:rootdir: {docdir}/..
:rootdir: {docdir}/../..
include::{rootdir}/includes/mp.adoc[]
Expand Down Expand Up @@ -128,7 +128,7 @@ In the current example, Helidon container will be launched prior test. The _Bean
[source,java]
.Code sample
----
include::{sourcedir}/mp/TestingNgSnippets.java[tag=snippet_1, indent=0]
include::{sourcedir}/mp/testing/TestingNgSnippets.java[tag=snippet_1, indent=0]
----
<1> Start the Helidon container.
<2> Set disabled Bean Discovery for the current test class.
Expand All @@ -143,7 +143,7 @@ To test `@RequestScoped` bean with JaxRs support:
[source,java]
.Test `RequestScoped` bean
----
include::{sourcedir}/mp/TestingNgSnippets.java[tag=snippet_2, indent=0]
include::{sourcedir}/mp/testing/TestingNgSnippets.java[tag=snippet_2, indent=0]
----
<1> Start the Helidon container.
<2> Set disabled Bean discovery.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
:description: Helidon Testing with JUnit5
:keywords: helidon, mp, test, testing, junit
:feature-name: Testing with JUnit
:rootdir: {docdir}/..
:rootdir: {docdir}/../..
include::{rootdir}/includes/mp.adoc[]
Expand All @@ -32,6 +32,7 @@ include::{rootdir}/includes/mp.adoc[]
- <<Maven Coordinates, Maven Coordinates>>
- <<Usage, Usage>>
- <<Examples, Examples>>
- <<Mock Support, Mock Support>>
- <<Additional Information, Additional Information>>
- <<Reference, Reference>>
Expand Down Expand Up @@ -136,7 +137,7 @@ In the current example, Helidon container will be launched prior test. The _Bean
[source,java]
.Code sample
----
include::{sourcedir}/mp/TestingSnippets.java[tag=snippet_1, indent=0]
include::{sourcedir}/mp/testing/TestingSnippets.java[tag=snippet_1, indent=0]
----
<1> Start the Helidon container.
<2> Set disabled Bean Discovery for the current test class.
Expand All @@ -151,13 +152,78 @@ To test `@RequestScoped` bean with JaxRs support:
[source,java]
.Test `RequestScoped` bean
----
include::{sourcedir}/mp/TestingSnippets.java[tag=snippet_2, indent=0]
include::{sourcedir}/mp/testing/TestingSnippets.java[tag=snippet_2, indent=0]
----
<1> Start the Helidon container.
<2> Set disabled Bean discovery.
<3> Add JaxRs support to the current test class.
<4> Define a `RequestScoped` bean.
== Mock Support
This section describes how to mock objects using Helidon API and in a second phase, using pure CDI.
=== Helidon Mock Support
Helidon has its own API to use mocking with test classes annotated with `@HelidonTest`.
==== Maven Coordinates
To enable Helidon Mock Support add the following dependency to your project’s pom.xml.
[source,xml]
----
<dependency>
<groupId>io.helidon.microprofile.testing</groupId>
<artifactId>helidon-microprofile-testing-mocking</artifactId>
<scope>test</scope>
</dependency>
----
==== API
It consists of one annotation named `@MockBean`, designed to be used on fields and parameters. The implementation
relies only on CDI and thus it works with either JUnit or TestNG. The annotation has a parameter `answers` used
to set the default answer for the mocked beans.
==== Example
[source,java]
.Code sample
----
include::{sourcedir}/mp/testing/HelidonMockingSnippets.java[tag=snippet_1, indent=0]
----
<1> `service` field annotated with `@MockBean` and `Answers.CALLS_REAL_METHODS` for default answers.
<2> Test the mocked service with real method response.
<3> Test the mocked service with modified behavior.
=== Mocking objects with pure CDI
CDI can be used to enable mocking, the following example shows how to mock a service and inject it in a JAX-RS resource.
Let's consider a simple service `FooService` with a dummy method `FooService#getFoo` that return `foo` as a `String`.
And a resource where the service is injected and expose an endpoint at `/foo` that get the `String` provided by the service.
[source,java]
.Code sample
----
include::{sourcedir}/mp/testing/CDIMockingSnippets.java[tag=snippet_1, indent=0]
----
<1> A simple `foo` Service.
<2> Inject the service into the resource.
This example uses Mockito to create mock instances before each test. The method mockFooService is a CDI producer method
that adds the mock instance as an @Alternative in order to replace the FooService singleton.
[source,java]
.Code sample
----
include::{sourcedir}/mp/testing/CDIMockingSnippets.java[tag=snippet_2, indent=0]
----
<1> Set priority to 1 because of `@Alternative`.
<2> Set `fooService` to a new mock before each tests.
<3> This makes `FooResource` inject the mock instead of the default singleton.
<4> Test that the mock is injected with modified behavior.
<5> Test the real method behavior.
== Additional Information
* https://medium.com/helidon/testing-helidon-9df2ea14e22[Official blog article about Helidon and JUnit usage]
Expand Down
1 change: 1 addition & 0 deletions docs/src/main/asciidoc/sitegen.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ backend:
value: "analytics"
- type: "MENU"
title: "Testing"
dir: "testing"
glyph:
type: "icon"
value: "thumbs_up_down"
Expand Down
104 changes: 104 additions & 0 deletions docs/src/main/java/io/helidon/docs/mp/testing/CDIMockingSnippets.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Copyright (c) 2024 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.helidon.docs.mp.testing;

import io.helidon.microprofile.testing.junit5.HelidonTest;

import jakarta.annotation.Priority;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Alternative;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.client.WebTarget;
import jakarta.ws.rs.core.Response;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Answers;
import org.mockito.Mockito;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.mockito.Mockito.when;

@SuppressWarnings("ALL")
class CDIMockingSnippets {

// tag::snippet_1[]
@ApplicationScoped
public class FooService { // <1>

public String getFoo() {
return "foo";
}
}

@Path("/foo")
public class FooResource {

@Inject
private FooService fooService; // <2>

@GET
public String getFoo() {
return fooService.getFoo();
}
}
// end::snippet_1[]

// tag::snippet_2[]
@HelidonTest
@Priority(1) // <1>
class FooTest {

@Inject
private WebTarget target;

private FooService fooService;

@BeforeEach
void initMock() {
fooService = Mockito.mock(FooService.class, Answers.CALLS_REAL_METHODS); // <2>
}

@Produces
@Alternative
FooService mockFooService() {
return fooService; // <3>
}

@Test
void testMockedService() {
when(fooService.getFoo()).thenReturn("bar"); // <4>

Response response = target.path("/foo").request().get();

assertThat(response.getStatus(), is(200));
assertThat(response.readEntity(String.class), is("bar"));
}

@Test
void testService() {
Response response = target.path("/foo").request().get(); // <5>

assertThat(response.getStatus(), is(200));
assertThat(response.readEntity(String.class), is("foo"));
}
}
// end::snippet_2[]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright (c) 2024 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.helidon.docs.mp.testing;

import io.helidon.microprofile.testing.junit5.AddBean;
import io.helidon.microprofile.testing.junit5.HelidonTest;
import io.helidon.microprofile.testing.mocking.MockBean;

import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.client.WebTarget;

import org.junit.jupiter.api.Test;
import org.mockito.Answers;
import org.mockito.Mockito;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;

@SuppressWarnings("ALL")
class HelidonMockingSnippets {

// tag::snippet_1[]
@HelidonTest
@AddBean(MockBeanAnswerTest.Resource.class)
@AddBean(MockBeanAnswerTest.Service.class)
class MockBeanAnswerTest {

@MockBean(answer = Answers.CALLS_REAL_METHODS) // <1>
private Service service;
@Inject
private WebTarget target;

@Test
void injectionTest() {
String response = target.path("/test").request().get(String.class);
assertThat(response, is("Not Mocked")); // <2>
Mockito.when(service.test()).thenReturn("Mocked");
response = target.path("/test").request().get(String.class);
assertThat(response, is("Mocked")); // <3>
}

@Path("/test")
public static class Resource {

@Inject
private Service service;

@GET
public String test() {
return service.test();
}
}

static class Service {

String test() {
return "Not Mocked";
}

}
}
// end::snippet_1[]
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.helidon.docs.mp;
package io.helidon.docs.mp.testing;

import io.helidon.microprofile.config.ConfigCdiExtension;
import io.helidon.microprofile.testing.testng.AddBean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.helidon.docs.mp;
package io.helidon.docs.mp.testing;

import io.helidon.microprofile.config.ConfigCdiExtension;
import io.helidon.microprofile.testing.junit5.AddBean;
Expand Down

0 comments on commit abba8b5

Please sign in to comment.