Quarkus - Testing Your Application

Learn how to test your Quarkus Application.This guide covers:

  • Testing in JVM mode

  • Testing in native mode

  • Injection of resources into tests

1. Prerequisites

To complete this guide, you need:

  • less than 15 minutes

  • an IDE

  • JDK 1.8+ installed with JAVA_HOME configured appropriately

  • Apache Maven 3.5.3+

  • The completed greeter application from the Getting Started Guide

2. Architecture

In this guide, we expand on the initial test that was created as part of the Getting Started Guide.We cover injection into tests and also how to test native executables.

3. Solution

We recommend that you follow the instructions in the next sections and create the application step by step.However, you can go right to the completed example.

Clone the Git repository: git clone https://github.com/quarkusio/quarkus-quickstarts.git, or download an archive.

The solution is located in the getting-started-testing directory.

This guide assumes you already have the completed application from the getting-started directory.

4. Recap of HTTP based Testing in JVM mode

If you have started from the Getting Started example you should already have a completed test, including the correctpom.xml setup.

In the pom.xml file you should see 2 test dependencies:

  1. <dependency>
  2. <groupId>io.quarkus</groupId>
  3. <artifactId>quarkus-junit5</artifactId>
  4. <scope>test</scope>
  5. </dependency>
  6. <dependency>
  7. <groupId>io.rest-assured</groupId>
  8. <artifactId>rest-assured</artifactId>
  9. <scope>test</scope>
  10. </dependency>

quarkus-junit5 is required for testing, as it provides the @QuarkusTest annotation that controls the testing framework.rest-assured is not required but is a convenient way to test HTTP endpoints, we also provide integration that automaticallysets the correct URL so no configuration is required.

Because we are using JUnit 5, the version of the Surefire Maven Pluginmust be set, as the default version does not support Junit 5:

  1. <plugin>
  2. <artifactId>maven-surefire-plugin</artifactId>
  3. <version>${surefire-plugin.version}</version>
  4. <configuration>
  5. <systemProperties>
  6. <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
  7. </systemProperties>
  8. </configuration>
  9. </plugin>

We also set the java.util.logging system property to make sure tests will use the correct logmanager.

The project should also contain a simple test:

  1. package org.acme.quickstart;
  2. import io.quarkus.test.junit.QuarkusTest;
  3. import org.junit.jupiter.api.Test;
  4. import java.util.UUID;
  5. import static io.restassured.RestAssured.given;
  6. import static org.hamcrest.CoreMatchers.is;
  7. @QuarkusTest
  8. public class GreetingResourceTest {
  9. @Test
  10. public void testHelloEndpoint() {
  11. given()
  12. .when().get("/hello")
  13. .then()
  14. .statusCode(200)
  15. .body(is("hello"));
  16. }
  17. @Test
  18. public void testGreetingEndpoint() {
  19. String uuid = UUID.randomUUID().toString();
  20. given()
  21. .pathParam("name", uuid)
  22. .when().get("/hello/greeting/{name}")
  23. .then()
  24. .statusCode(200)
  25. .body(is("hello " + uuid));
  26. }
  27. }

This test uses HTTP to directly test our REST endpoint. When the test is run the application will be started beforethe test is run.

4.1. Controlling the test port

While Quarkus will listen on port 8080 by default, when running tests it defaults to 8081. This allows you to runtests while having the application running in parallel.

Changing the test portYou can configure the port used by tests by configuring quarkus.http.test-port in your application.properties:
  1. quarkus.http.test-port=8083

Quarkus also provides RestAssured integration that updates the default port used by RestAssured before the tests are run,so no additional configuration should be required.

4.2. Injecting a URI

It is also possible to directly inject the URL into the test which can make is easy to use a different client. This isdone via the @TestHTTPResource annotation.

Let’s write a simple test that shows this off to load some static resources. First create a simple HTML file insrc/main/resources/META-INF/resources/index.html :

  1. <html>
  2. <head>
  3. <title>Testing Guide</title>
  4. </head>
  5. <body>
  6. Information about testing
  7. </body>
  8. </html>

We will create a simple test to ensure that this is being served correctly:

  1. package org.acme.quickstart;
  2. import java.io.ByteArrayOutputStream;
  3. import java.io.IOException;
  4. import java.io.InputStream;
  5. import java.net.URL;
  6. import java.nio.charset.StandardCharsets;
  7. import org.junit.jupiter.api.Assertions;
  8. import org.junit.jupiter.api.Test;
  9. import io.quarkus.test.common.http.TestHTTPResource;
  10. import io.quarkus.test.junit.QuarkusTest;
  11. @QuarkusTest
  12. public class StaticContentTest {
  13. @TestHTTPResource("index.html") (1)
  14. URL url;
  15. @Test
  16. public void testIndexHtml() throws Exception {
  17. try (InputStream in = url.openStream()) {
  18. String contents = readStream(in);
  19. Assertions.assertTrue(contents.contains("<title>Testing Guide</title>"));
  20. }
  21. }
  22. private static String readStream(InputStream in) throws IOException {
  23. byte[] data = new byte[1024];
  24. int r;
  25. ByteArrayOutputStream out = new ByteArrayOutputStream();
  26. while ((r = in.read(data)) > 0) {
  27. out.write(data, 0, r);
  28. }
  29. return new String(out.toByteArray(), StandardCharsets.UTF_8);
  30. }
  31. }
1This annotation allows you to directly inject the URL of the Quarkus instance, the value of the annotation will be the path component of the URL

For now @TestHTTPResource allows you to inject URI, URL and String representations of the URL.

5. Injection into tests

So far we have only covered integration style tests that test the app via HTTP endpoints, but what if we want to do unittesting and test our beans directly?

Quarkus supports this by allowing you to inject CDI beans into your tests via the @Inject annotation (in fact, tests inQuarkus are full CDI beans, so you can use all CDI functionality). Let’s create a simple test that tests the greetingservice directly without using HTTP:

  1. package org.acme.quickstart;
  2. import javax.inject.Inject;
  3. import org.junit.jupiter.api.Assertions;
  4. import org.junit.jupiter.api.Test;
  5. import io.quarkus.test.junit.QuarkusTest;
  6. @QuarkusTest
  7. public class GreetingServiceTest {
  8. @Inject (1)
  9. GreetingService service;
  10. @Test
  11. public void testGreetingService() {
  12. Assertions.assertEquals("hello Quarkus", service.greeting("Quarkus"));
  13. }
  14. }
1The GreetingService bean will be injected into the test

6. Applying Interceptors to Tests

As mentioned above Quarkus tests are actually full CDI beans, and as such you can apply CDI interceptors as you wouldnormally. As an example, if you want a test method to run within the context of a transaction you can simply apply the@Transactional annotation to the method and the transaction interceptor will handle it.

In addition to this you can also create your own test stereotypes. For example we could create a @TransactionalQuarkusTestas follows:

  1. @QuarkusTest
  2. @Stereotype
  3. @Transactional
  4. @Retention(RetentionPolicy.RUNTIME)
  5. @Target(ElementType.TYPE)
  6. public @interface TransactionalQuarkusTest {
  7. }

If we then apply this annotation to a test class it will act as if we had applied both the @QuarkusTest and@Transactional annotations, e.g.:

  1. @TransactionalQuarkusTest
  2. public class TestStereotypeTestCase {
  3. @Inject
  4. UserTransaction userTransaction;
  5. @Test
  6. public void testUserTransaction() throws Exception {
  7. Assertions.assertEquals(Status.STATUS_ACTIVE, userTransaction.getStatus());
  8. }
  9. }

7. Mock Support

Quarkus supports the use of mock objects using the CDI @Alternative mechanism.To use this simply override the bean you wish to mock with a class in the src/test/java directory, and put the @Alternative and @Priority(1) annotations on the bean.Alternatively, a convenient io.quarkus.test.Mock stereotype annotation could be used.This built-in stereotype declares @Alternative, @Priority(1) and @Dependent.For example if I have the following service:

  1. @ApplicationScoped
  2. public class ExternalService {
  3. public String service() {
  4. return "external";
  5. }
  6. }

I could mock it with the following class in src/test/java:

  1. @Mock
  2. @ApplicationScoped (1)
  3. public class MockExternalService extends ExternalService {
  4. @Override
  5. public String service() {
  6. return "mock";
  7. }
  8. }
1Overrides the @Dependent scope declared on the @Mock stereotype.

It is important that the alternative be present in the src/test/java directory rather than src/main/java, as otherwiseit will take effect all the time, not just when testing.

Note that at present this approach does not work with native image testing, as this would required the test alternativesto be baked into the native image.

8. Test Bootstrap Configuration Options

There are a few system properties that can be used to tune the bootstrap of the test, specifically its classpath.

  • quarkus-bootstrap-offline - (boolean) if set by the user, depending on the value, will enable or disable the offline mode for the Maven artifact resolver used by the bootstrap to resolve the deployment dependencies of the Quarkus extensions used in the test. If the property is not set to any value, the artifact resolver will use the system’s default (user’s settings.xml).

  • quarkus-workspace-discovery - (boolean) controls whether the bootstrap artifact resolver should look for the test dependencies among the projects in the current workspace and use their output (classes) directories when setting up the classpath for the test to run. The default value is true.

  • quarkus-classpath-cache - (boolean) enables or disables the bootstrap classpath cache. With the number of the project dependencies growing, the dependency resolution will take more time which could at some point become annoying. The Quarkus bootstrap allows to cache the resolved classpath and store it in the output directory of the project. The cached classpath will be recalculated only after any of the pom.xml file in the workspace has been changed. The cache directory is also removed each time the project’s output directory is cleaned, of course. The default value is true.

9. Native Executable Testing

It is also possible to test native executables using @NativeImageTest. This supports all the features mentioned in thisguide except injecting into tests (and the native executable runs in a separate non-JVM process this is not really possible).

This is covered in the Native Executable Guide.