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.6.2+

  • 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](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 correct pom.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 automatically sets the correct URL so no configuration is required.

Because we are using JUnit 5, the version of the Surefire Maven Plugin must 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. <systemPropertyVariables>
  6. <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
  7. <maven.home>${maven.home}</maven.home>
  8. </systemPropertyVariables>
  9. </configuration>
  10. </plugin>

We also set the java.util.logging.manager system property to make sure tests will use the correct logmanager and maven.home to ensure that custom configuration from ${maven.home}/conf/settings.xml is applied (if any).

The project should also contain a simple test:

  1. package org.acme.getting.started.testing;
  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 before the 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 run tests while having the application running in parallel.

Changing the test port

You can configure the ports used by tests by configuring quarkus.http.test-port for HTTP and quarkus.http.test-ssl-port for HTTPS in your application.properties:

  1. quarkus.http.test-port=8083
  2. quarkus.http.test-ssl-port=8446

0 will result in the use of a random port (assigned by the operating system).

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 is done 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 in src/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.getting.started.testing;
  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. Testing a specific endpoint

Both RESTassured and @TestHTTPResource allow you to specify the endpoint class you are testing rather than hard coding a path. This currently supports both JAX-RS endpoints, Servlets and Reactive Routes. This makes it a lot easier to see exactly which endpoints a given test is testing.

For the purposes of these examples I am going to assume we have an endpoint that looks like the following:

  1. @Path("/hello")
  2. public class GreetingResource {
  3. @GET
  4. @Produces(MediaType.TEXT_PLAIN)
  5. public String hello() {
  6. return "hello";
  7. }
  8. }
This currently does not support the @ApplicationPath() annotation to set the JAX-RS context path. Use the quarkus.resteasy.path config value instead if you want a custom context path.

5.1. TestHTTPResource

You can the use the io.quarkus.test.common.http.TestHTTPEndpoint annotation to specify the endpoint path, and the path will be extracted from the provided endpoint. If you also specify a value for the TestHTTPResource endpoint it will be appended to the end of the endpoint path.

  1. package org.acme.getting.started.testing;
  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.TestHTTPEndpoint;
  10. import io.quarkus.test.common.http.TestHTTPResource;
  11. import io.quarkus.test.junit.QuarkusTest;
  12. @QuarkusTest
  13. public class StaticContentTest {
  14. @TestHTTPEndpoint(GreetingResource.class) (1)
  15. @TestHTTPResource
  16. URL url;
  17. @Test
  18. public void testIndexHtml() throws Exception {
  19. try (InputStream in = url.openStream()) {
  20. String contents = readStream(in);
  21. Assertions.assertTrue(contents.equals("hello"));
  22. }
  23. }
  24. private static String readStream(InputStream in) throws IOException {
  25. byte[] data = new byte[1024];
  26. int r;
  27. ByteArrayOutputStream out = new ByteArrayOutputStream();
  28. while ((r = in.read(data)) > 0) {
  29. out.write(data, 0, r);
  30. }
  31. return new String(out.toByteArray(), StandardCharsets.UTF_8);
  32. }
  33. }
1Because GreetingResource is annotated with @Path(“/hello”) the injected URL will end with /hello.

5.2. RESTassured

To control the RESTassured base path (i.e. the default path that serves as the root for every request) you can use the io.quarkus.test.common.http.TestHTTPEndpoint annotation. This can be applied at the class or method level. To test out greeting resource we would do:

  1. package org.acme.getting.started.testing;
  2. import io.quarkus.test.junit.QuarkusTest;
  3. import io.quarkus.test.common.http.TestHTTPEndpoint;
  4. import org.junit.jupiter.api.Test;
  5. import java.util.UUID;
  6. import static io.restassured.RestAssured.given;
  7. import static org.hamcrest.CoreMatchers.is;
  8. @QuarkusTest
  9. @TestHTTPEndpoint(GreetingResource.class) (1)
  10. public class GreetingResourceTest {
  11. @Test
  12. public void testHelloEndpoint() {
  13. given()
  14. .when().get() (2)
  15. .then()
  16. .statusCode(200)
  17. .body(is("hello"));
  18. }
  19. }
1This tells RESTAssured to prefix all requests with /hello.
2Note we don’t need to specify a path here, as /hello is the default for this test

6. 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 unit testing 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 in Quarkus are full CDI beans, so you can use all CDI functionality). Let’s create a simple test that tests the greeting service directly without using HTTP:

  1. package org.acme.getting.started.testing;
  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

7. Applying Interceptors to Tests

As mentioned above Quarkus tests are actually full CDI beans, and as such you can apply CDI interceptors as you would normally. 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 @TransactionalQuarkusTest as 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. }

8. Enrichment via QuarkusTest*Callback

Alternatively or additionally to an interceptor, you can enrich all your @QuarkusTest classes by implementing the following callback interfaces:

  • io.quarkus.test.junit.callback.QuarkusTestBeforeAllCallback

  • io.quarkus.test.junit.callback.QuarkusTestBeforeEachCallback

  • io.quarkus.test.junit.callback.QuarkusTestAfterEachCallback

Such a callback implementation has to be registered as a “service provider” as defined by java.util.ServiceLoader.

E.g. the following sample callback:

  1. package org.acme.getting.started.testing;
  2. import io.quarkus.test.junit.callback.QuarkusTestBeforeEachCallback;
  3. import io.quarkus.test.junit.callback.QuarkusTestMethodContext;
  4. public class MyQuarkusTestBeforeEachCallback implements QuarkusTestBeforeEachCallback {
  5. @Override
  6. public void beforeEach(QuarkusTestMethodContext context) {
  7. System.out.println("Executing " + context.getTestMethod());
  8. }
  9. }

has to be registered via src/main/resources/META-INF/services/io.quarkus.test.junit.callback.QuarkusTestBeforeEachCallback as follows:

  1. org.acme.getting.started.testing.MyQuarkusTestBeforeEachCallback
It is possible to read annotations from the test class or method to control what the callback shall be doing.
While it is possible to use JUnit Jupiter callback interfaces like BeforeEachCallback, you might run into classloading issues because Quarkus has to run tests in a custom classloader which JUnit is not aware of.

9. Testing Different Profiles

So far in all our examples we only start Quarkus once for all tests. Before the first test is run Quarkus will boot, then all tests will run, then Quarkus will shutdown at the end. This makes for a very fast testing experience however it is a bit limited as you can’t test different configurations.

To get around this Quarkus supports the idea of a test profile. If a test has a different profile to the previously run test then Quarkus will be shut down and started with the new profile before running the tests. This is obviously a bit slower, as it adds a shutdown/startup cycle to the test time, but gives a great deal of flexibility.

In order to reduce the amount of times Quarkus needs to restart it is recommended that you place all tests that need a specific profile into their own package, and then run tests alphabetically.

9.1. Writing a Profile

To implement a test profile we need to implement io.quarkus.test.junit.QuarkusTestProfile:

  1. package org.acme.getting.started.testing;
  2. import java.util.Collections;
  3. import java.util.Map;
  4. import java.util.Set;
  5. import io.quarkus.test.junit.QuarkusTestProfile;
  6. public class MockGreetingProfile implements QuarkusTestProfile {
  7. @Override
  8. public Map<String, String> getConfigOverrides() { (1)
  9. return Collections.singletonMap("quarkus.resteasy.path","/api");
  10. }
  11. @Override
  12. public Set<Class<?>> getEnabledAlternatives() { (2)
  13. return Collections.singleton(MockGreetingService.class);
  14. }
  15. @Override
  16. public String getConfigProfile() { (3)
  17. return "test";
  18. }
  19. }
1This method allows us to override configuration properties. Here we are changing the JAX-RS root path.
2This method allows us to enable CDI @Alternative beans. This makes it easy to mock out certain beans functionality.
3This can be used to change the config profile. As this default is test this does nothing, but is included for completeness.

Now we have defined our profile we need to include it on our test class. We do this with @TestProfile(MockGreetingProfile.class).

All the test profile config is stored in a single class, which makes it easy to tell if the previous test ran with the same configuration.

10. Mock Support

Quarkus supports the use of mock objects using two different approaches. You can either use CDI alternatives to mock out a bean for all test classes, or use QuarkusMock to mock out beans on a per test basis.

10.1. 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 otherwise it 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 alternatives to be baked into the native image.

10.2. Mocking using QuarkusMock

The io.quarkus.test.junit.QuarkusMock class can be used to temporarily mock out any normal scoped bean. If you use this method in a @BeforeAll method the mock will take effect for all tests on the current class, while if you use this in a test method the mock will only take effect for the duration of the current test.

This method can be used for any normal scoped CDI bean (e.g. @ApplicationScoped, @RequestScoped etc, basically every scope except @Singleton and @Dependent).

An example usage could look like:

  1. @QuarkusTest
  2. public class MockTestCase {
  3. @Inject
  4. MockableBean1 mockableBean1;
  5. @Inject
  6. MockableBean2 mockableBean2;
  7. @BeforeAll
  8. public static void setup() {
  9. MockableBean1 mock = Mockito.mock(MockableBean1.class);
  10. Mockito.when(mock.greet("Stuart")).thenReturn("A mock for Stuart");
  11. QuarkusMock.installMockForType(mock, MockableBean1.class); (1)
  12. }
  13. @Test
  14. public void testBeforeAll() {
  15. Assertions.assertEquals("A mock for Stuart", mockableBean1.greet("Stuart"));
  16. Assertions.assertEquals("Hello Stuart", mockableBean2.greet("Stuart"));
  17. }
  18. @Test
  19. public void testPerTestMock() {
  20. QuarkusMock.installMockForInstance(new BonjourGreeter(), mockableBean2); (2)
  21. Assertions.assertEquals("A mock for Stuart", mockableBean1.greet("Stuart"));
  22. Assertions.assertEquals("Bonjour Stuart", mockableBean2.greet("Stuart"));
  23. }
  24. @ApplicationScoped
  25. public static class MockableBean1 {
  26. public String greet(String name) {
  27. return "Hello " + name;
  28. }
  29. }
  30. @ApplicationScoped
  31. public static class MockableBean2 {
  32. public String greet(String name) {
  33. return "Hello " + name;
  34. }
  35. }
  36. public static class BonjourGreeter extends MockableBean2 {
  37. @Override
  38. public String greet(String name) {
  39. return "Bonjour " + name;
  40. }
  41. }
  42. }
1As the injected instance is not available here we use installMockForType, this mock is used for both test methods
2We use installMockForInstance to replace the injected bean, this takes effect for the duration of the test method.

Note that there is no dependency on Mockito, you can use any mocking library you like, or even manually override the objects to provide the behaviour you require.

10.2.1. Further simplification with @InjectMock

Building on the features provided by QuarkusMock, Quarkus also allows users to effortlessly take advantage of Mockito for mocking the beans supported by QuarkusMock. This functionality is available via the @io.quarkus.test.junit.mockito.InjectMock annotation which is available in the quarkus-junit5-mockito dependency.

Using @InjectMock, the previous example could be written as follows:

  1. @QuarkusTest
  2. public class MockTestCase {
  3. @InjectMock
  4. MockableBean1 mockableBean1; (1)
  5. @InjectMock
  6. MockableBean2 mockableBean2;
  7. @BeforeEach
  8. public void setup() {
  9. Mockito.when(mockableBean1.greet("Stuart")).thenReturn("A mock for Stuart"); (2)
  10. }
  11. @Test
  12. public void firstTest() {
  13. Assertions.assertEquals("A mock for Stuart", mockableBean1.greet("Stuart"));
  14. Assertions.assertEquals(null, mockableBean2.greet("Stuart")); (3)
  15. }
  16. @Test
  17. public void secondTest() {
  18. Mockito.when(mockableBean2.greet("Stuart")).thenReturn("Bonjour Stuart"); (4)
  19. Assertions.assertEquals("A mock for Stuart", mockableBean1.greet("Stuart"));
  20. Assertions.assertEquals("Bonjour Stuart", mockableBean2.greet("Stuart"));
  21. }
  22. @ApplicationScoped
  23. public static class MockableBean1 {
  24. public String greet(String name) {
  25. return "Hello " + name;
  26. }
  27. }
  28. @ApplicationScoped
  29. public static class MockableBean2 {
  30. public String greet(String name) {
  31. return "Hello " + name;
  32. }
  33. }
  34. }
1@InjectMock results in a mock being and is available in test methods of the test class (other test classes are not affected by this)
2The mockableBean1 is configured here for every test method of the class
3Since the mockableBean2 mock has not been configured, it will return the default Mockito response.
4In this test the mockableBean2 is configured, so it returns the configured response.

Although the test above is good for showing the capabilities of @InjectMock, it is not a good representation of a real test. In a real test we would most likely configure a mock, but then test a bean that uses the mocked bean. Here is an example:

  1. @QuarkusTest
  2. public class MockGreetingServiceTest {
  3. @InjectMock
  4. GreetingService greetingService;
  5. @Test
  6. public void testGreeting() {
  7. when(greetingService.greet()).thenReturn("hi");
  8. given()
  9. .when().get("/greeting")
  10. .then()
  11. .statusCode(200)
  12. .body(is("hi")); (1)
  13. }
  14. @Path("greeting")
  15. public static class GreetingResource {
  16. final GreetingService greetingService;
  17. public GreetingResource(GreetingService greetingService) {
  18. this.greetingService = greetingService;
  19. }
  20. @GET
  21. @Produces("text/plain")
  22. public String greet() {
  23. return greetingService.greet();
  24. }
  25. }
  26. @ApplicationScoped
  27. public static class GreetingService {
  28. public String greet(){
  29. return "hello";
  30. }
  31. }
  32. }
1Since we configured greetingService as a mock, the GreetingResource which uses the GreetingService bean, we get the mocked response instead of the response of the regular GreetingService bean

10.2.2. Using Spies instead of Mocks with @InjectSpy

Building on the features provided by InjectMock, Quarkus also allows users to effortlessly take advantage of Mockito for spying on the beans supported by QuarkusMock. This functionality is available via the @io.quarkus.test.junit.mockito.InjectSpy annotation which is available in the quarkus-junit5-mockito dependency.

Sometimes when testing you only need to verify that a certain logical path was taken, or you only need to stub out a single method’s response while still executing the rest of the methods on the Spied clone. Please see Mockito documentation for more details on Spy partial mocks. In either of those situations a Spy of the object is preferable. Using @InjectSpy, the previous example could be written as follows:

  1. @QuarkusTest
  2. public class SpyGreetingServiceTest {
  3. @InjectSpy
  4. GreetingService greetingService;
  5. @Test
  6. public void testDefaultGreeting() {
  7. given()
  8. .when().get("/greeting")
  9. .then()
  10. .statusCode(200)
  11. .body(is("hello"));
  12. Mockito.verify(greetingService, Mockito.times(1)).greet(); (1)
  13. }
  14. @Test
  15. public void testOverrideGreeting() {
  16. when(greetingService.greet()).thenReturn("hi"); (2)
  17. given()
  18. .when().get("/greeting")
  19. .then()
  20. .statusCode(200)
  21. .body(is("hi")); (3)
  22. }
  23. @Path("greeting")
  24. public static class GreetingResource {
  25. final GreetingService greetingService;
  26. public GreetingResource(GreetingService greetingService) {
  27. this.greetingService = greetingService;
  28. }
  29. @GET
  30. @Produces("text/plain")
  31. public String greet() {
  32. return greetingService.greet();
  33. }
  34. }
  35. @ApplicationScoped
  36. public static class GreetingService {
  37. public String greet(){
  38. return "hello";
  39. }
  40. }
  41. }
1Instead of overriding the value, we just want to ensure that the greet method on our GreetingService was called by this test.
2Here we are telling the Spy to return “hi” instead of “hello”. When the GreetingResource requests the greeting from GreetingService we get the mocked response instead of the response of the regular GreetingService bean
3We are verifying that we get the mocked response from the Spy.

10.2.3. Using @InjectMock with @RestClient

The @RegisterRestClient registers the implementation of the rest-client at runtime, and because the bean needs to be a regular scope, you have to annotate your interface with @ApplicationScoped.

  1. @Path("/")
  2. @ApplicationScoped
  3. @RegisterRestClient
  4. public interface GreetingService {
  5. @GET
  6. @Path("/hello")
  7. @Produces(MediaType.TEXT_PLAIN)
  8. String hello();
  9. }

For the test class here is an example:

  1. @QuarkusTest
  2. public class GreetingResourceTest {
  3. @InjectMock
  4. @RestClient (1)
  5. GreetingService greetingService;
  6. @Test
  7. public void testHelloEndpoint() {
  8. Mockito.when(greetingService.hello()).thenReturn("hello from mockito");
  9. given()
  10. .when().get("/hello")
  11. .then()
  12. .statusCode(200)
  13. .body(is("hello from mockito"));
  14. }
  15. }
1Indicate that this injection point is meant to use an instance of RestClient.

10.3. Mocking with Panache

If you are using the quarkus-hibernate-orm-panache or quarkus-mongodb-panache extensions, check out the Hibernate ORM with Panache Mocking and MongoDB with Panache Mocking documentation for the easiest way to mock your data access.

11. Testing Security

If you are using Quarkus Security, check out the Testing Security section for information on how to easily test security features of the application.

12. Starting services before the Quarkus application starts

A very common need is to start some services on which your Quarkus application depends, before the Quarkus application starts for testing. To address this need, Quarkus provides @io.quarkus.test.common.QuarkusTestResource and io.quarkus.test.common.QuarkusTestResourceLifecycleManager.

By simply annotating any test in the test suite with @QuarkusTestResource, Quarkus will run the corresponding QuarkusTestResourceLifecycleManager before any tests are run. A test suite is also free to utilize multiple @QuarkusTestResource annotations, in which case all the corresponding QuarkusTestResourceLifecycleManager objects will be run before the tests.

Quarkus provides a few implementations of QuarkusTestResourceLifecycleManager out of the box (see io.quarkus.test.h2.H2DatabaseTestResource which starts an H2 database, or io.quarkus.test.kubernetes.client.KubernetesMockServerTestResource which starts a mock Kubernetes API server), but it is common to create custom implementations to address specific application needs. Common cases include starting docker containers using Testcontainers (an example of which can be found here), or starting a mock HTTP server using Wiremock (an example of which can be found here).

13. Native Executable Testing

It is also possible to test native executables using @NativeImageTest. This supports all the features mentioned in this guide 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.

14. Running @QuarkusTest from an IDE

Most IDEs offer the possibility to run a selected class as JUnit test directly. For this you should set a few properties in the settings of your chosen IDE:

  • java.util.logging.manager (see Logging Guide)

  • maven.home (only if there are any custom settings in ${maven.home}/conf/settings.mxl, see Maven Guide)

  • maven.settings (in case a custom version of settings.xml file should be used for the tests)

14.1. Eclipse separate JRE definition

Copy your current “Installed JRE” definition into a new one, where you will add the properties as a new VM arguments:

  • -Djava.util.logging.manager=org.jboss.logmanager.LogManager

  • -Dmaven.home=<path-to-your-maven-installation>

Use this JRE definition as your Quarkus project targeted runtime and the workaround will be applied to any “Run as JUnit” configuration.

14.2. VSCode “run with” configuration

The settings.json placed in the root of your project directory or in the workspace will need the workaround in your test configuration:

  1. "java.test.config": [
  2. {
  3. "name": "quarkusConfiguration",
  4. "vmargs": [ "-Djava.util.logging.manager=org.jboss.logmanager.LogManager -Dmaven.home=<path-to-your-maven-installation> ..." ],
  5. ...
  6. },
  7. ...
  8. ]

14.3. IntelliJ JUnit template

Nothing needed in IntelliJ because the IDE will pick the systemPropertyVariables from the surefire plugin configuration in pom.xml.