Testing
This page details Testing.
Example
@FusionSupport
class MyTest {
@Test
void run(@Fusion final MyService service) {
// ...
}
}
Runner flavors
There are two runner flavors:
-
FusionSupportwhich starts and stops the container per test class, -
MonoFusionSupportwhich starts and stops the container per JVM - faster but does not isolate all classes.
|
TIP
|
using a custom JUnit 5
Then simply replace fusion annotation by Alternatively you can register a test |
Note that if you need to exclude some module from the discovery (which uses ServiceLoader), mono extension readsyupiik.fusion.mono.modules.discovery.excluded system property which takes a comma separated list of fully qualified names.
Testing a Launcher application
Particularly for CLI applications (using an Launcher and an Awaiter getting Args injected for example), you can use @FusionCLITest as a replacement of @Test. It will enable you to get an automatic execution of Launcher based on the annotation args value and to get injected Stdout and Stderr to validate the outputs.
@FusionCLITest(args = {"test", "run"})
void run(final Stdout stdout) {
assertEquals("...", stdout.content().strip());
}
|
TIP
|
|
Utilities
Additionally to the main container friendly extensions, fusion-testing also provides a few utilities to ease writing tests.
TestClient
TestClient is a bean which is active when you use fusion-json at least and likely fusion-http-server.
It autoconfigures the base URI of the server - even when port is set to 0 (random) and enables to use HttpClient like API with:
-
Logging of the request/response (uses
fusion-httpclientis present), -
Hides exception handling (synchronous API, aka
send) to simplify code writing in tests, -
Provide
jsonRpcJUnit5 integration to ease JSON-RPC tests.
@Test
void myTest(@Fusion final TestClient client) { (1)
final var res = client.jsonRpcRequest( (2)
"my.jsonrpc.method", (3)
Map.of("id", id.value())); (4)
final var order = res
.asJsonRpc() (5)
.success() (6)
.as(Order.class); (7)
assertAll(
() -> assertEquals(200, res.statusCode()),
() -> assertInsertedOrder(order, id, res::body));
}
-
Inject the
TestClientas a standard fusion bean - requires a fusion support annotation, - Call a JSON-RPC method just providing its method name and parameters,
- The JSON-RPC method name,
- The JSON-RPC method params,
-
Convert the
HttpResponseto a JSON-RPC response helper, -
Asserts the JSON-RPC response has no
error, -
Maps the
resultas an object - compatible withfusion-json.
Before/After method task
JUnit5 has @BeforeEach/@AfterEach callbacks but they are not per methods and related to the class - state - more then the test itself. It is also not directly related to fusion IoC beans.
To solve that pitfall, @Task enables to reference some code - task - which can be a fusion bean or not and call it before or after a test method execution.
Here is how it can look like:
@Test
@Task(phase = BEFORE, value = InsertMyData.class) (1)
@Task(phase = AFTER, value = CleanMyData.class) (2)
void run(@TaskResult(InsertMyData.class) final MyData data) { (3)
// ...
}
-
Before the test run the bean
InsertMyData, -
After the test run the bean
CleanMyData, -
Inject the result of
InsertMyDataas first parameter.
@Task(value) must implement Task.Supplier, which can be a java Supplier<T> - which, for BEFORE tasks, will enable to inject some state as parameter referencing the bean class. It can also be a Runnable - mainly for AFTER tasks.
Here is a sample BEFORE task:
@DefaultScoped
public class BeforeTask implements Task.Supplier<MyData> {
private final MyService service;
// constructor...
@Override
public MyData get() {
return service.createData();
}
}
An after task will often be like:
@DefaultScoped
public class AfterTask implements Task.Supplier<String> {
private final MyService service;
// constructor...
@Override
public void run() {
service.clean();
}
}
What is interesting is that, since tasks are beans, you can use an @ApplicationScoped bean to link both before/after tasks and for example store the before identifiers to delete in the after phase.
Replacing beans in tests
fusion-testing ships TestingModule and ListingPredicateModule which let you replace application beans with test doubles. TestingModule scans the generated $FusionBean classes of the current project (from target/classes and target/test-classes) and swaps the matched bean for your replacement, so you can override any @ApplicationScoped/@DefaultScoped bean without reflection.
final var container = ConfiguringContainer.of()
.disableModule(/**/ TestingModule.class) (1)
.start();
-
Register your own
TestingModuleinstance (or aListingPredicateModule) to replace the matching beans before the container starts.