Skip to content

Commit

Permalink
doc: split documentation into sections (#866)
Browse files Browse the repository at this point in the history
  • Loading branch information
sdelamo authored Oct 15, 2023
1 parent 5966cb5 commit 528daee
Show file tree
Hide file tree
Showing 34 changed files with 608 additions and 661 deletions.
199 changes: 0 additions & 199 deletions src/main/docs/guide/junit5.adoc
Original file line number Diff line number Diff line change
@@ -1,199 +0,0 @@
=== Setting up JUnit 5

To get started using JUnit 5 you need the following dependencies in your build configuration:

.build.gradle
[source,groovy,subs="attributes"]
----
dependencies {
testAnnotationProcessor "io.micronaut:micronaut-inject-java"
...
testImplementation("org.junit.jupiter:junit-jupiter-api")
testImplementation("io.micronaut.test:micronaut-test-junit5")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
testImplementation("org.junit.jupiter:junit-jupiter-engine")
}
// use JUnit 5 platform
test {
useJUnitPlatform()
}
----

NOTE: If you plan to define mock beans you will also need `inject-groovy` on your `testCompile` classpath or `inject-java` for Java or Kotlin (this should already be configured if you used `mn create-app`) and the `testAnnotationProcessor`.

Or for Maven:

.pom.xml
[source,xml]
----
<dependency>
<groupId>io.micronaut.test</groupId>
<artifactId>micronaut-test-junit5</artifactId>
<scope>test</scope>
</dependency>
----

=== Writing a Micronaut Test with JUnit 5

Let's take a look at an example using JUnit 5. Consider you have the following interface:

.The MathService Interface
[source,java]
----
include::{junit5tests}/MathService.java[]
----

And a simple implementation that computes the value times 4 and is defined as Micronaut bean:

.The MathService implementation
[source,java]
----
include::{junit5tests}/MathServiceImpl.java[]
----

You can define the following test to test the implementation:

.The MathService specification
[source,groovy]
----
include::{junit5tests}/MathServiceTest.java[]
----

<1> The test is declared as Micronaut test with `@MicronautTest`
<2> The `@Inject` annotation is used to inject the bean
<3> The test itself tests the injected bean


=== Environments, Classpath Scanning etc.

include::src/main/docs/guide/includes/environments-classpath-scanning.adoc[]

=== Transaction semantics

include::src/main/docs/guide/includes/transaction.adoc[]

=== Using Mockito Mocks

Now let's say you want to replace the implementation with a Mockito Mock. You can do so by defining a method that returns a mock and is annotated with `@MockBean`, for example:

.The MathService specification
[source,java]
----
include::{junit5tests}/MathMockServiceTest.java[]
----

<1> The `@MockBean` annotation is used to indicate the method returns a mock bean. The value to the method is the type being replaced.
<2> Mockito's `mock(..)` method creates the actual mock
<3> The Mock is injected into the test
<4> Mockito is used to verify the mock is called

Note that because the bean is an inner class of the test, it will be active only for the scope of the test. This approach allows you to define beans that are isolated per test class.

=== Mocking Collaborators

Note that in most cases you won't define a `@MockBean` and then inject it only to verify interaction with the Mock directly, instead the Mock will be a collaborator within your application. For example say you have a `MathController`:

.The MathController
[source,java]
----
include::{junit5tests}/MathController.java[]
----

The above controller uses the `MathService` to expose a `/math/compute/{number}` endpoint. See the following example for a test that tests interaction with the mock collaborator:

.Mocking Collaborators
[source,java]
----
include::{junit5tests}/MathCollaboratorTest.java[]
----

<1> Like the previous example a Mock is defined using `@MockBean`
<2> This time we inject an instance of `HttpClient` to test the controller.
<3> We invoke the controller and retrieve the result
<4> The interaction with mock collaborator is verified.

The way this works is that `@MicronautTest` will inject the `Mock(..)` instance into the test, but the controller will have a proxy that points to the `Mock(..)` instance injected. For each iteration of the test the mock is refreshed (in fact it uses Micronaut's built in `RefreshScope`).

For Factory injected beans, you can use Factory Replacement to inject Mocks. Refer to the https://docs.micronaut.io/latest/guide/index.html#_factory_replacement[factory replacement documentation] for more information.

=== Using `@Requires` on Tests

Since `@MicronautTest` turns tests into beans themselves, it means you can use the `@Requires` annotation on the test to enable/disable tests. For example:

[source,java]
----
@MicronautTest
@Requires(env = "my-env")
class RequiresTest {
...
}
----

The above test will only run if `my-env` is active (you can activate it by passing the system property `micronaut.environments`).

=== Defining Additional Test Specific Properties

You can define additional test specific properties using the `@Property` annotation. The following example demonstrates usage:

.Using `@Property`
[source,java]
----
include::{junit5tests}/PropertyValueTest.java[]
----

Note that when a `@Property` is defined at the test method level, it causes a `RefreshEvent` to be triggered which will update any `@ConfigurationProperties` related to the property.

Alternatively you can specify additional `propertySources` in any supported format (YAML, JSON, Java properties file etc.) using the `@MicronautTest` annotation:

.Using `propertySources` stored in files
[source,java]
----
include::{junit5tests}/PropertySourceTest.java[]
----

The above example expects a file located at `src/test/resources/io/micronaut/junit5/myprops.properties`. You can however use a prefix to indicate where the file should be searched for. The following are valid values:

* `file:myprops.properties` - A relative path to a file somewhere on the file system
* `classpath:myprops.properties` - A file relative to the root of the classpath
* `myprops.properties` - A file relative on the classpath relative to the test being run.

If you need more dynamic property definition or the property you want to define requires some setup then you can implement the api:test.support.TestPropertyProvider[] interface in your test and do whatever setup is necessary then return the properties you want to expose the the application.

For example:

.Using the `TestPropertyProvider` interface
[source,java]
----
include::{junit5tests}/PropertySourceMapTest.java[]
----

NOTE: When using `TestPropertyProvider` your test must be declared with JUnit's `@TestInstance(TestInstance.Lifecycle.PER_CLASS)` annotation.

=== Refreshing injected beans based on `@Requires` upon properties changes

include::src/main/docs/guide/includes/refreshing-requires.adoc[]

.Combining `@Requires` and `@Property` in a `@Refreshable` test class.
[source,java]
----
include::{junit5tests}/PropertyValueRequiresTest.java[]
----

=== Testing External Servers

You can write integration tests that test external servers in a couple of different ways.

One way is with the `micronaut.test.server.executable` property that allows you to specify the location of an executable JAR or native image of a server that should be started and shutdown for the lifecycle of test.

In this case Micronaut Test will replace the regular server with an instance of api:test.support.server.TestExecutableEmbeddedServer[] that executes the process to start the server and closes the process when the test ends.

For example:

.Using `micronaut.test.server.executable`
[source,java]
----
include::{junit5tests}/server/ProcessServerTest.java[tags="executable"]
----

Alternatively if you have independently started an `EmbeddedServer` instance programmatically you can also specify the URL to the server with the `micronaut.test.server.url` property.
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
You can define additional test specific properties using the `@Property` annotation. The following example demonstrates usage:

.Using `@Property`
[source,java]
----
include::{junit5tests}/PropertyValueTest.java[]
----

Note that when a `@Property` is defined at the test method level, it causes a `RefreshEvent` to be triggered which will update any `@ConfigurationProperties` related to the property.

Alternatively you can specify additional `propertySources` in any supported format (YAML, JSON, Java properties file etc.) using the `@MicronautTest` annotation:

.Using `propertySources` stored in files
[source,java]
----
include::{junit5tests}/PropertySourceTest.java[]
----

The above example expects a file located at `src/test/resources/io/micronaut/junit5/myprops.properties`. You can however use a prefix to indicate where the file should be searched for. The following are valid values:

* `file:myprops.properties` - A relative path to a file somewhere on the file system
* `classpath:myprops.properties` - A file relative to the root of the classpath
* `myprops.properties` - A file relative on the classpath relative to the test being run.
If you need more dynamic property definition or the property you want to define requires some setup then you can implement the api:test.support.TestPropertyProvider[] interface in your test and do whatever setup is necessary then return the properties you want to expose the the application.

For example:

.Using the `TestPropertyProvider` interface
[source,java]
----
include::{junit5tests}/PropertySourceMapTest.java[]
----

NOTE: When using `TestPropertyProvider` your test must be declared with JUnit's `@TestInstance(TestInstance.Lifecycle.PER_CLASS)` annotation.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include::src/main/docs/guide/includes/environments-classpath-scanning.adoc[]
24 changes: 24 additions & 0 deletions src/main/docs/guide/junit5/junit5MockingCollaborators.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Note that in most cases you won't define a `@MockBean` and inject it, only to verify interaction with the Mock directly. Instead, the Mock will be a collaborator within your application. For example say you have a `MathController`:

.The MathController
[source,java]
----
include::{junit5tests}/MathController.java[]
----

The above controller uses the `MathService` to expose a `/math/compute/{number}` endpoint. See the following example for a test that tests interaction with the mock collaborator:

.Mocking Collaborators
[source,java]
----
include::{junit5tests}/MathCollaboratorTest.java[]
----

<1> Like the previous example a Mock is defined using `@MockBean`
<2> This time we inject an instance of `HttpClient` to test the controller.
<3> We invoke the controller and retrieve the result
<4> The interaction with mock collaborator is verified.

The way this works is that `@MicronautTest` will inject the `Mock(..)` instance into the test, but the controller will have a proxy that points to the `Mock(..)` instance injected. For each iteration of the test the mock is refreshed (in fact it uses Micronaut's built in `RefreshScope`).

For Factory injected beans, you can use Factory Replacement to inject Mocks. Refer to the https://docs.micronaut.io/latest/guide/index.html#_factory_replacement[factory replacement documentation] for more information.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
include::src/main/docs/guide/includes/refreshing-requires.adoc[]

.Combining `@Requires` and `@Property` in a `@Refreshable` test class.
[source,java]
----
include::{junit5tests}/PropertyValueRequiresTest.java[]
----
15 changes: 15 additions & 0 deletions src/main/docs/guide/junit5/junit5TestingExternalServers.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
You can write integration tests that test external servers in a couple of different ways.

One way is with the `micronaut.test.server.executable` property that allows you to specify the location of an executable JAR or native image of a server that should be started and shutdown for the lifecycle of test.

In this case Micronaut Test will replace the regular server with an instance of api:test.support.server.TestExecutableEmbeddedServer[] that executes the process to start the server and closes the process when the test ends.

For example:

.Using `micronaut.test.server.executable`
[source,java]
----
include::{junit5tests}/server/ProcessServerTest.java[tags="executable"]
----

Alternatively if you have independently started an `EmbeddedServer` instance programmatically you can also specify the URL to the server with the `micronaut.test.server.url` property.
1 change: 1 addition & 0 deletions src/main/docs/guide/junit5/junit5TransactionSemantics.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include::src/main/docs/guide/includes/transaction.adoc[]
12 changes: 12 additions & 0 deletions src/main/docs/guide/junit5/junit5UsingRequiresOnTests.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Since `@MicronautTest` turns tests into beans themselves, it means you can use the `@Requires` annotation on the test to enable/disable tests. For example:

[source,java]
----
@MicronautTest
@Requires(env = "my-env")
class RequiresTest {
...
}
----

The above test will only run if `my-env` is active (you can activate it by passing the system property `micronaut.environments`).
33 changes: 33 additions & 0 deletions src/main/docs/guide/junit5/settingUpJUnit5.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
To get started using JUnit 5 you need the following dependencies in your build configuration:

.build.gradle
[source,groovy,subs="attributes"]
----
dependencies {
testAnnotationProcessor "io.micronaut:micronaut-inject-java"
...
testImplementation("org.junit.jupiter:junit-jupiter-api")
testImplementation("io.micronaut.test:micronaut-test-junit5")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
testImplementation("org.junit.jupiter:junit-jupiter-engine")
}
// use JUnit 5 platform
test {
useJUnitPlatform()
}
----

NOTE: If you plan to define mock beans you will also need `inject-groovy` on your `testCompile` classpath or `inject-java` for Java or Kotlin (this should already be configured if you used `mn create-app`) and the `testAnnotationProcessor`.

Or for Maven:

.pom.xml
[source,xml]
----
<dependency>
<groupId>io.micronaut.test</groupId>
<artifactId>micronaut-test-junit5</artifactId>
<scope>test</scope>
</dependency>
----
14 changes: 14 additions & 0 deletions src/main/docs/guide/junit5/usingMockitoMocks.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Now let's say you want to replace the implementation with a Mockito Mock. You can do so by defining a method that returns a mock and is annotated with `@MockBean`, for example:

.The MathService specification
[source,java]
----
include::{junit5tests}/MathMockServiceTest.java[]
----

<1> The `@MockBean` annotation is used to indicate the method returns a mock bean. The value to the method is the type being replaced.
<2> Mockito's `mock(..)` method creates the actual mock
<3> The Mock is injected into the test
<4> Mockito is used to verify the mock is called

Note that because the bean is an inner class of the test, it will be active only for the scope of the test. This approach allows you to define beans that are isolated per test class.
28 changes: 28 additions & 0 deletions src/main/docs/guide/junit5/writingAMicronautTestWithJUnit5.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
Let's take a look at an example using JUnit 5. Consider you have the following interface:

.The MathService Interface
[source,java]
----
include::{junit5tests}/MathService.java[]
----

And a simple implementation that computes the value times 4 and is defined as a Micronaut bean:

.The MathService implementation
[source,java]
----
include::{junit5tests}/MathServiceImpl.java[]
----

You can define the following test to test the implementation:

.The MathService specification
[source,groovy]
----
include::{junit5tests}/MathServiceTest.java[]
----

<1> The test is declared as Micronaut test with `@MicronautTest`
<2> The `@Inject` annotation is used to inject the bean
<3> The test itself tests the injected bean

Loading

0 comments on commit 528daee

Please sign in to comment.