Contents
Overview
Helidon provides built-in test support for Helidon testing with JUnit 5.
Maven Coordinates
To enable Helidon Testing Framework add the following dependency to your project’s pom.xml (see Managing Dependencies).
<dependency>
<groupId>io.helidon.webserver.testing.junit5</groupId>
<artifactId>helidon-webserver-testing-junit5</artifactId>
<scope>test</scope>
</dependency>Usage
Helidon provides a rich set of extensions based on JUnit 5 for Helidon WebServer testing. Testing can be done with automatic server start-up, configuration, and shutdown. Testing can also be done without full server start-up with DirectClient when no real sockets are created.
API
There are two main annotations that you can use to test Helidon WebServer.
@ServerTestis an integration test annotation that starts the server (opens ports) and provides client injection pre-configured for the server port(s).@RoutingTestis a unit test annotation that does not start the server and does not open ports but provides a direct client (with the same API as the usual network client) to test routing.
The additional annotation @Socket can be used to qualify the injection of parameters into test constructors or methods, such as to obtain a client configured for the named socket.
The following table lists the supported types of parameters for the @SetUpRoute annotated methods. Such methods MUST be static and may have any name. The @SetUpRoute annotation has value with socket name (to customize the setup for a different socket).
Parameter type - supported class of a parameter
Annotation - which annotations support this parameter
Modules - which webserver extension modules support this signature
@SetUpRoute annotated methods.| Parameter Type | Annotation | Modules | Notes |
|---|---|---|---|
HttpRouting.Builder | @ServerTest, @RoutingTest | ||
HttpRules | @ServerTest, @RoutingTest | Same as HttpRouting.Builder, only routing setup | |
Router.RouterBuilder<?> | @ServerTest, @RoutingTest | ||
SocketListener.Builder | @ServerTest | ||
WebSocketRouting.Builder | @ServerTest, @RoutingTest | websocket |
In addition, a static method annotated with @SetUpServer can be defined for @ServerTest, which has a single parameter of WebServerConfig.Builder.
The following table lists the injectable types (through constructor or method injection).
Type - type that can be injected
Socket - if checked, you can use the
@Socketannotation to obtain a value specific to that named socketAnnotation - which annotations support this injection
Modules - which WebServer extension modules support this injection
Notes - additional details
| Type | Socket? | Annotation | Modules | Notes |
WebServer | @ServerTest | Server instance (already started) | ||
URI | x | @ServerTest | URI pointing to a port of the webserver | |
SocketHttpClient | x | @ServerTest | This client allows you to send anything in order to test for bad requests or other issues. | |
Http1Client | x | @ServerTest | ||
DirectClient | x | @RoutingTest | Implements Http1Client API | |
WsClient | x | @ServerTest | websocket | |
DirectWsClient | x | @RoutingTest | websocket | Implements WsClient API |
Extensions can enhance the features for the module helidon-testing-junit5-webserver to support additional protocols.
Examples
You can create the following test to validate that the server returns the correct response:
@ServerTest
class MyServerTest {
final Http1Client client;
MyServerTest(Http1Client client) {
this.client = client;
}
@SetUpRoute
static void routing(HttpRouting.Builder builder) {
Main.routing(builder);
}
@Test
void testRootRoute() {
try (Http1ClientResponse response = client
.get("/greet")
.request()) {
assertThat(response.status(), is(Status.OK_200));
}
}
}- Use
@ServerTestto trigger the testing framework. - Inject
Http1Clientfor the test. - SetUp routing for the test.
- Regular
JUnittest method. - Call the
clientto obtain server response - Perform the necessary assertions.
To trigger the framework to start and configure the server, annotate the testing class with the @ServerTest annotation.
In this test, the Http1Client client is used, which means that the framework will create, configure, and inject this object as a parameter to the constructor.
To set up routing, a static method annotated with @SetUpRoute is present. The framework uses this method to inject the configured routing to the subject of testing – in the current case, the Quickstart application.
As everything above is performed by the testing framework, regular unit tests can be done. After completing all tests, the testing framework will shut down the server.
Routing Tests
If there is no need to set up and run a server, a DirectClient client can be used. It is a testing client that bypasses HTTP transport and directly invokes the router.
@RoutingTest and DirectClient.@RoutingTest
class MyRoutingTest {
final Http1Client client;
MyRoutingTest(DirectClient client) {
this.client = client;
}
@SetUpRoute
static void routing(HttpRouting.Builder builder) {
Main.routing(builder);
}
@Test
void testRootRoute() {
try (Http1ClientResponse response = client
.get("/greet")
.request()) {
JsonObject json = response.as(JsonObject.class);
assertThat(json.getString("message"), is("Hello World!"));
}
}
}- Use
@RoutingTestto trigger the testing framework. - Inject
DirectClientfor the test. - SetUp routing for the test.
- A regular
JUnittest method. - Call the
clientto obtain server response. - Perform the necessary assertions.
If only routing tests are required, this is a "lighter" way of testing because the framework will not configure and run the full Helidon server. This way, no real ports will be opened. All the communication will be done through DirectClient, which makes the tests very effective.
It is required to annotate the test class with the @RoutingTest annotation to trigger the server to do the configuration. Thus, it will inject the DirectClient client, which can then be used in unit tests.
Routing is configured the same way as in full server testing using the @SetUpRoute annotation.
Additional Information
WebSocket Testing
If WebSocket testing is required, there is an additional module for it. It is necessary to include the following Maven dependency to the Project’s pom file:
<dependency>
<groupId>io.helidon.testing.junit5</groupId>
<artifactId>helidon-testing-junit5-websocket</artifactId>
<scope>test</scope>
</dependency>WebSocket Testing Example
The WebSocket Testing extension adds support for routing configuration and injection of WebSocket related artifacts, such as WebSockets and DirectWsClient in Helidon unit tests.
@ServerTest
class WsSocketTest {
static final ServerSideListener WS_LISTENER = new ServerSideListener();
final WsClient wsClient;
WsSocketTest(WsClient wsClient) {
this.wsClient = wsClient;
}
@SetUpRoute
static void routing(WsRouting.Builder ws) {
ws.endpoint("/testWs", WS_LISTENER);
}
@Test
void testWsEndpoint() {
ClientSideListener clientListener = new ClientSideListener();
wsClient.connect("/testWs", clientListener);
assertThat(clientListener.message, is("ws"));
}
}- Declare
WsClientand later inject it in the constructor. - Using @SetUpRoute, create WebSocket routing and assign a serverside listener.
- Test the WebSocket endpoint using the regular @Test annotation.
- Create and assign the clientside listener.
- Check if the received message is correct.
ClientSideListener helper class.static class ClientSideListener implements WsListener {
volatile String message;
volatile Throwable error;
@Override
public void onOpen(WsSession session) {
session.send("hello", true);
}
@Override
public void onMessage(WsSession session, String text, boolean last) {
message = text;
session.close(WsCloseCodes.NORMAL_CLOSE, "End");
}
@Override
public void onError(WsSession session, Throwable t) {
error = t;
}
}- Send "Hello" when a connection is opened.
- Save the message when received and close the connection.
- React on an error.
The WebSocket ClientSideListener is also a helper class that implements WsListener and is very straightforward:
ServerSideListener helper class.static class ServerSideListener implements WsListener {
volatile String message;
@Override
public void onMessage(WsSession session, String text, boolean last) {
message = text;
session.send("ws", true);
}
}- Send "ws" on a received message.
The testing class should be annotated with @RoutingTest only if routing tests are required without real port opening. Instead of WsClient, use DirectWsClient.