This is a cache of https://developer.ibm.com/tutorials/quarkus-basics-05/. It is a snapshot of the page as it appeared on 2025-12-05T02:53:43.998+0000.
In this tutorial, you will go beyond the basics and explore the different testing features that Quarkus provides. You have already seen simple REST tests, so now the focus is on understanding the different types of tests Quarkus supports and when to use them.
You will learn the difference between plain JUnit unit tests, Quarkus integration tests with @QuarkusTest, and production-like tests with @QuarkusIntegrationTest. You will also see how Quarkus enables continuous testing in dev mode, how to test error handling, and how to replace dependencies with mocks during tests.
Prerequisites
Before you begin, make sure that you have a Quarkus project from an earlier tutorial (such as the "Building REST APIs with Quarkus" or "Mastering data persistence with Quarkus" tutorial) that you can use as a base. You will add tests to this existing project to explore the different testing approaches in Quarkus.
Testing layers in Quarkus
Testing in Quarkus builds on top of JUnit 5, which many Java developers already know. What Quarkus adds is an easy way to test your application in different modes, from isolated unit tests to full integration tests with Dev Services. Knowing which test type to use saves time and keeps your test suite efficient. Think of it as three layers:
@Test (plain JUnit). These are regular unit tests that don’t start Quarkus. They are fast and used for testing pure Java logic (for example, helper classes or algorithms).
@Quarkusteststarts. Quarkus in dev/test mode before you run your tests, as real runtime tests. This lets you test REST endpoints, database access, and CDI injection inside a real Quarkus runtime. tests run in the same JVM as your app.
@QuarkusIntegrationTestRuns. Run your tests against the packaged application (JAR or native image). This simulates how your app will behave in production. These tests take longer, but they give you confidence that your deployment will work.
Step 1. Write a plain unit test
Not all code depends on Quarkus. Many parts of your application are just plain Java logic, like string operations, math calculations, or utility classes. You can test these pieces with JUnit directly. These tests are very fast because they don’t start any extra framework; only the JVM runs them.
When we say Quarkus isn’t “running,” we mean that:
No REST endpoints are started.
No CDI (dependency injection) context is available.
No database is started.
Dev Services is not started.
Only your test class and the code it calls are loaded by the JVM.
This is the simplest and fastest type of test. It’s great for checking business rules or helper methods without waiting for Quarkus to boot.
For example, this test runs instantly and does not start Quarkus:
Step 2. Run tests inside Quarkus with @QuarkusTest
Sometimes you want to test more than plain Java logic. You want to check that your REST endpoints respond correctly, that CDI injections work, or that your database queries return real results. For this, you need Quarkus itself to start.
When you use @QuarkusTest, Quarkus boots up in test mode before running your tests. This is not the full production startup, but a lightweight mode that still gives you almost everything:
The Quarkus runtime starts, similar to dev mode.
CDI (Contexts and Dependency Injection) is active, so you can inject beans and services.
REST endpoints are deployed and can be called by your tests.
Dev Services can start automatically, for example PostgreSQL or Kafka, so you test against real services without manual setup.
The key difference from a plain JUnit test is that now you are testing your application inside a running Quarkus environment.
In the following example, Quarkus starts your application, exposes the /greeting endpoint, and your test verifies the result by using RESTAssured.
Quarkus handles the request and returns a JSON response, just like in real life.
@QuarkusTest is perfect for integration tests that verify how your application behaves when its components work together.
Step 3. Run a production-like test with @QuarkusIntegrationTest
Unit tests and @QuarkusTest give you fast feedback, but they don’t run your application exactly like it will run in production. Sometimes problems only appear after packaging, such as missing resources in the JAR, configuration issues, or native image differences. That’s why Quarkus also supports integration tests that run against the packaged application.
When you annotate a test with @QuarkusIntegrationTest, Quarkus does something special:
First, Quarkus builds your application as a JAR or a native binary.
Then, Quarkus launches that packaged application as an external process.
Finally, the tests connect to that running process by using real HTTP requests.
This means your tests no longer run “inside” Quarkus like they do with @QuarkusTest. Instead, they talk to the application from the outside. Just like a client or user would.
In the following example, GreetingResourceIT extends GreetingResourceTest. This means that the same endpoint tests are run again, but this time against the packaged application.
package com.ibm.developer;
import io.quarkus.test.junit.QuarkusIntegrationTest;
@QuarkusIntegrationTestpublicclassGreetingResourceITextendsGreetingResourceTest {
// Runs the same tests as GreetingResourceTest,// but against the packaged application.
}
Copy codeCopied!
Behind the scenes, this is what's happening:
Quarkus packages your app (./mvnw package -Dnative if you build a native binary, otherwise a JAR).
The packaged app is started as if it were running in production.
Your test class connects over HTTP to that running process.
Once all tests are done, the process is stopped.
This is slower than @QuarkusTest, because building and starting the application takes time. But it gives you high confidence that your deployment will work the same way in production.
Use this test:
Before you release your app to production.
When you are testing native builds.
For end-to-end tests or smoke tests that confirm that the packaged app works.
Step 4. Test error handling
Applications don’t only need to succeed. Sometimes we also want them to fail in the right way. Testing error responses ensures that your users see correct status codes and messages.
The following example confirms that trying to delete a non-existing resource gives the expected error.
@TestpublicvoidtestDeleteUnknownFruit() {
given()
.when().delete("/fruits/9999")
.then()
.statusCode(404); // Not Found
}
Copy codeCopied!
Step 5. Use continuous testing in dev mode
You have already seen continuous testing mode in earlier tutorials in this series, so this is a quick recap. Normally, developers write code, save it, and then run tests manually. This means that you only see failures after you decide to check. With Quarkus, tests run continuously in dev mode. Every time that you change code or configuration, Quarkus reruns the tests automatically. This gives you instant feedback and helps you fix problems before they grow.
When you run your app in dev mode (quarkus dev), Quarkus:
Watches your project files for changes (Java code, resources, configuration).
Quickly recompiles only what changed.
Figures out which tests are affected by those changes. For example:
If you edit a REST resource, only the tests that hit that endpoint need to run.
If you edit a utility class used by many components, more tests might need to rerun.
Runs those tests automatically and shows results in the terminal.
This smart tracking is why continuous testing feels much faster than rerunning the entire test suite every time.
Try it out! Start dev mode:
quarkus dev
Copy codeCopied!
You can also control test behavior in the dev mode console:
Press r to rerun all tests.
Press o to toggle test output.
Press p to pause continuous testing.
Continuous testing in dev mode saves time, reduces errors, and makes testing feel like a natural part of coding, not an extra chore.
Step 6. Try mocking, an advanced testing techniques
In real projects, your code often depends on external systems, like remote REST APIs, message brokers, or third-party services. When testing, you usually don’t want to call those real systems. They might be slow, unavailable or cost money to use. Instead, you use mocks, which are lightweight replacements that simulate the behavior of the real service.
Quarkus makes mocking easy through CDI alternatives. You can write a fake version of a service and tell Quarkus to use it only during tests. This way, your tests stay fast and reliable.
Let’s say you have a service that fetches a greeting from a remote API:
package com.ibm.developer;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScopedpublicclassGreetingService {
public String getGreeting() {
// Imagine this calls a remote APIreturn"Hello from remote service!";
}
}
WireMock: Simulate HTTP services with predefined responses.
Dev Services: Automatically start lightweight versions of Kafka, databases, or other infrastructure.
Mockito: Still possible if you prefer classic mocking libraries.
Mocking lets you test your code in isolation. You control dependencies, avoid flaky tests, and keep your test suite fast. This is especially important in enterprise applications where external systems may not always be available.
Summary
You learned how Quarkus provides three distinct testing layers each serving different purposes in your testing strategy: plain JUnit unit tests for fast isolated logic, @QuarkusTest for integration tests within a running Quarkus environment, and @QuarkusIntegrationTest for production-like tests against packaged applications. You also discovered how to use CDI alternatives for mocking dependencies and how continuous testing provides immediate feedback during development, helping you choose the right test approach for different scenarios.
About cookies on this siteOur websites require some cookies to function properly (required). In addition, other cookies may be used with your consent to analyze site usage, improve the user experience and for advertising.For more information, please review your cookie preferences options. By visiting our website, you agree to our processing of information as described in IBM’sprivacy statement. To provide a smooth navigation, your cookie preferences will be shared across the IBM web domains listed here.