When building REST APIs or microservices in Micronaut, unit tests validate individual components.
But real-world applications need integration tests to ensure everything works together — routes, controllers, services, repositories, security, and more.
✅ Micronaut provides excellent built-in support for integration testing using its Embedded Server.
- No external deployment required.
- Fast startup.
- Lightweight but production-like environment.
In this post, you’ll learn step-by-step how to write integration tests using Embedded Server.

Why Use Embedded Server for Integration Tests?
Problem | Solution |
---|---|
Hard to test full stack | Embedded server simulates full app |
Need HTTP-level testing | Embedded server exposes real HTTP endpoints |
Test routes/controllers/services | All are active and wired |
With Micronaut’s Embedded Server:
- Tests run inside the Micronaut runtime.
- Full dependency injection works.
- Real HTTP clients can call actual routes.
🚀 Project Setup
Let’s assume you already have a basic Micronaut project.
If not, you can generate one via Micronaut CLI:
1 2 3 4 |
mn create-app com.kscodes.micronaut.demo --build maven |
🛠 Maven Dependencies
Micronaut testing support is included by default.
Verify these dependencies exist:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<dependency> <groupId>io.micronaut.test</groupId> <artifactId>micronaut-test-junit5</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <scope>test</scope> </dependency> |
If you’re using Spock, replace with micronaut-test-spock
.
🏫 Example Application
We’ll test this simple controller:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package com.kscodes.micronaut.demo.controllers; import io.micronaut.http.annotation.*; @Controller("/hello") public class HelloController { @Get("/{name}") public String sayHello(String name) { return "Hello, " + name + "!"; } } |
🧪 Writing Integration Tests
Let’s create a test class that:
- Boots Micronaut using Embedded Server.
- Calls the REST endpoint.
- Asserts the response.
✅ Example Integration Test
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
package com.kscodes.micronaut.demo; import io.micronaut.http.client.HttpClient; import io.micronaut.http.client.annotation.Client; import io.micronaut.test.extensions.junit5.annotation.MicronautTest; import jakarta.inject.Inject; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; @MicronautTest class HelloControllerIntegrationTest { @Inject @Client("/") HttpClient httpClient; @Test void testHelloEndpoint() { String response = httpClient.toBlocking() .retrieve("/hello/Ketan"); assertEquals("Hello, Ketan!", response); } } |
✅ What’s happening here:
@MicronautTest
— boots Embedded Server automatically.@Client("/")
— injects an HTTP client targeting the running server.- Actual HTTP request made via
retrieve()
. - Response asserted with JUnit.
The server runs in-memory, so you don’t need external deployment.
⚙ Configuration for Embedded Server
Micronaut automatically uses application-test.yml
profile when running tests.
You can override config for integration tests like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# src/test/resources/application-test.yml micronaut: application: name: demo datasources: default: url: jdbc:h2:mem:devDb;DB_CLOSE_DELAY=-1 driverClassName: org.h2.Driver username: sa password: |
✅ This isolates your tests from production configuration.
🧪 Testing Services and Repositories
Micronaut fully wires all beans during integration tests.
Example test with service:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
package com.kscodes.micronaut.demo; import com.kscodes.micronaut.demo.services.GreetingService; import io.micronaut.test.extensions.junit5.annotation.MicronautTest; import jakarta.inject.Inject; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; @MicronautTest class GreetingServiceTest { @Inject GreetingService greetingService; @Test void testGreeting() { String message = greetingService.greet("Ketan"); assertEquals("Hello, Ketan!", message); } } |
✅ You can inject any bean like controllers, services, repositories, even external clients.
🔬 Testing Full CRUD Integration
If you have database access via repositories, you can write full integration tests:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
package com.kscodes.micronaut.demo; import com.kscodes.micronaut.demo.entities.Student; import com.kscodes.micronaut.demo.repositories.StudentRepository; import io.micronaut.test.extensions.junit5.annotation.MicronautTest; import jakarta.inject.Inject; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; @MicronautTest(transactional = false) class StudentRepositoryIntegrationTest { @Inject StudentRepository studentRepository; @Test void testStudentRepository() { Student student = new Student(); student.setName("Ketan"); student.setEmail("ketan@kscodes.com"); Student saved = studentRepository.save(student); assertNotNull(saved.getId()); Student fetched = studentRepository.findById(saved.getId()).orElse(null); assertEquals("Ketan", fetched.getName()); } } |
✅ Micronaut supports in-memory H2 by default, making DB integration tests fast.
⚠ Common Mistakes to Avoid
Issue | Solution |
---|---|
@MicronautTest not working | Ensure micronaut-test-junit5 is in dependencies |
HTTP client fails | Use @Client("/") to target embedded server |
Database not available | Use in-memory H2 or test containers |
🧰 Best Practices
✅ Use Embedded Server for real HTTP-level tests
✅ Use application-test.yml
for isolation
✅ Use transactional tests unless testing async behavior
✅ Avoid hitting external services (mock them if needed)
✅ Use lightweight test data