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:
mn create-app com.kscodes.micronaut.demo --build maven
🛠 Maven Dependencies
Micronaut testing support is included by default.
Verify these dependencies exist:
io.micronaut.test
micronaut-test-junit5
test
org.junit.jupiter
junit-jupiter-engine
test
If you’re using Spock, replace with micronaut-test-spock.
🏫 Example Application
We’ll test this simple controller:
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
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:
# 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:
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:
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