mp.testing.testing.adoc Maven / Gradle / Ivy
The newest version!
///////////////////////////////////////////////////////////////////////////////
Copyright (c) 2020, 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.
///////////////////////////////////////////////////////////////////////////////
= Testing with JUnit5
:h1Prefix: MP
:pagename: testing
:description: Helidon Testing with JUnit5
:keywords: helidon, mp, test, testing, junit
:feature-name: Testing with JUnit
:rootdir: {docdir}/../..
include::{rootdir}/includes/mp.adoc[]
== Contents
- <>
- <>
- <>
- <>
- <>
- <>
- <>
== Overview
Helidon provides built-in test support for CDI testing in JUnit5.
include::{rootdir}/includes/dependencies.adoc[]
[source,xml]
----
io.helidon.microprofile.testing
helidon-microprofile-testing-junit5
test
----
== Usage
A test can be annotated with `io.helidon.microprofile.testing.junit5.HelidonTest` annotation to mark it as a
CDI test. This annotation will start the CDI container before any test method is invoked, and stop it after
the last method is invoked. This annotation also enables injection into the test class itself.
=== Usage - per method CDI container
A test can be annotated as follows:
`@HelidonTest(resetPerTest = true)`
This will change the behavior as follows:
- A new CDI container is created for each test method invocation
- annotations to add config, beans and extension can be added for each method in addition to the class
- you cannot inject fields or constructor parameters of the test class itself (as a single instance is shared by more containers)
- you can add `SeContainer` as a method parameter of any test method and you will get the current container
=== Usage - configuration
In addition to the `@AddConfig` and `@AddConfigBlock` annotations, you can also use
`@Configuration` to configure additional classpath properties config sources using `configSources`, and to
mark that a custom configuration is desired.
If `@Configuration(useExisting=true)`, the existing (or default) MicroProfile configuration would be used. In this case
it is important to set property `mp.initializer.allow=true` in order CDI container to start, when used with
`@HelidonTest`.
You can set up config in `@BeforeAll` method and register it with `ConfigProviderResolver` using MP Config APIs, and declare
`@Configuration(useExisting=true)`.
Note that this is not compatible with repeatable tests that use method sources that access CDI, as we must delay the CDI
startup to the test class instantiation (which is too late, as the method sources are already invoked by this time).
*If you want to use method sources that use CDI with repeatable tests, please do not use `@Configuration(useExisting=true)`*
=== Usage - added parameters and injection types
The following types are available for injection (when a single CDI container is used per test class):
- `WebTarget` - a JAX-RS client's target configured for the current hostname and port when `helidon-micorprofile-server` is on
the classpath
The following types are available as method parameters (in any type of Helidon tests):
- `WebTarget` - a JAX-RS client's target configured for the current hostname and port when `helidon-micorprofile-server` is on
the classpath
- `SeContainer` - the current container instance
== API
The annotations described in this section are inherited (for the non-repeatable ones), and additive (for repeatable).
So if you declare `@DisableDiscovery` on abstract class, all implementations will have discovery disabled, unless you
annotate the implementation class with `@DisableDiscovery(false)`.
If you declare `@AddBean` on both abstract class and implementation class, both beans will be added.
In addition to this simplification, the following annotations are supported:
|===
|Annotation | Usage
|`@io.helidon.microprofile.testing.junit5.AddBean`
|Used to add one or more beans to the container (if not part of a bean archive, or when discovery is disabled)
|`@io.helidon.microprofile.testing.junit5.AddExtension`
|Used to add one or more CDI extensions to the container (if not added through service loader, or when discovery is disabled)
|`@io.helidon.microprofile.testing.junit5.AddConfig`
|Used to add one or more configuration properties to MicroProfile config without the need of creating a `microprofile-config.properties` file
|Used `@io.helidon.microprofile.testing.junit5.DisableDiscovery`
|to disable automated discovery of beans and extensions
|Used `@io.helidon.microprofile.testing.junit5.DisableDiscovery`
|to disable automated discovery of beans and extensions
|Used `@io.helidon.microprofile.testing.junit5.AddJaxRs`
a|add JaxRs support to the test class. Only used with `@DisableDiscovery` annotation, otherwise an exception will be thrown. Automatically adds the following Beans and Extensions to the test class:
* `ServerCdiExtension`
* `JaxRsCdiExtension`
* `CdiComponentProvider`
* `org.glassfish.jersey.ext.cdi1x.internal.ProcessAllAnnotatedTypes`
* `org.glassfish.jersey.weld.se.WeldRequestScope`
|===
== Examples
In the current example, Helidon container will be launched prior test. The _Bean Discovery_ will be disabled. _MyBean_ will be added to the test, so that it can be injected. _ConfigCdiExtension_ will be enabled for this test. And finally, a configuration property will be added using `@AddConfig` annotation.
[source,java]
.Code sample
----
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.
<3> Add `MyBean` to current context.
<4> Add a configuration CDI extension to the current test.
<5> Add configuration properties.
<6> Inject `MyBean` as it is available in the CDI context.
<7> Run rests.
To test `@RequestScoped` bean with JaxRs support:
[source,java]
.Test `RequestScoped` bean
----
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]
----
io.helidon.microprofile.testing
helidon-microprofile-testing-mocking
test
----
==== 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]
== Reference
* https://junit.org/junit5/docs/current/user-guide/[JUnit 5 User Guide]