In the previous chapter, we created a Todo application using Java, Quarkus and Mongodb. In this chapter, lets to the same using Java and Micronaut.
Quarkus/Kotlin – Before moving on to the Java/Micronaut implementation, lets quickly have a look at the quarkus kotlin combination. Here is my repository for the same – faskan/todo-kotlin-quarkus (github.com). Unfortunately testcontainers for mongodb didn’t worked like I expected in Kotlin – Quarkus combination. And I am yet to figure a way to do that. I have raised a question for the same in stack overflow, let’s see if someone has faced similar problems. Other than that the implementation was pretty straight forward like we did before with Spring Boot and Kotlin. The only changes are the usage of different annotations like JAXRS in place of Spring Rest and Panache in place of Spring Data. You may refer to my previous post for more details about those annotations.
Micronaut/Java – Coming back to Micronaut, here is my repository with Micronaut/Java implementation – faskan/todo-micronaut-java (github.com). I bootstrapped my application using Micronaut launch console
The micronaut launch console is similar to Spring Initializr and Quarkus application generatorhttps://code.quarkus.io/
Here is my test case, it tries to save a Todo object and retrieves all the todos and verifies if the saved todo was present in the response.
@Testcontainers
public class TodoResourceIT {
@Container
private static MongoDBContainer mongoDBContainer =
new MongoDBContainer(DockerImageName.parse("mongo:4.2"));
private static EmbeddedServer embeddedServer;
private static HttpClient client;
@BeforeAll
public static void init() {
embeddedServer = ApplicationContext.run(EmbeddedServer.class, PropertySource.of(
"test", Map.of("mongodb.uri", mongoDBContainer.getReplicaSetUrl("micronaut"))
));
client = embeddedServer.getApplicationContext().getBean(HttpClient.class);
}
@Test
void shouldSaveTodo() throws JSONException {
HttpResponse httpResponse = client.toBlocking().exchange(request(), Todo.class);
assertEquals(HttpStatus.OK, httpResponse.getStatus());
String response = client.toBlocking()
.retrieve(HttpRequest.GET(embeddedServer.getURL()+"/api/todos"));
JSONAssert.assertEquals("""
[
{
"name": "test",
"description": "description"
}
]
""", response, JSONCompareMode.LENIENT);
}
private HttpRequest<Todo> request() {
return HttpRequest.POST(embeddedServer.getURL()+"/api/todos", new Todo("test", "description"));
}
}
Differences with Qaurkus implementation – With quarkus, we added @QuarkusTest annotation for out test, and Quarkus booted the application and test lifecycle made sure that the static mongodb test container was started before the execution of test cases. With Micronaut, an equivalent for @QuarkusTest is @MicronautTest, but unfortunately the lifecycle of the test execution does not spin up the static mongodb test container before execution of tests. I am not sure if it is a bug or an intentional behavior.
Alternate approach – So I had to take an alternate approach where in instead of using @MicronautTest annotation, I just wrote it as a normal unit test and booted the micronaut application in the @BeforeAll method. @TestContainer annotation is used to integrate testcontainers with Junit5. Here is the documentation for the same – Jupiter / JUnit 5 – Testcontainers.
The below snippet spins up a micronaut application with embedded server.
ApplicationContext.run(EmbeddedServer.class, PropertySource.of(
"test", Map.of("mongodb.uri", mongoDBContainer.getReplicaSetUrl("micronaut"))
))
The application context by default comes with a HttpClient bean that can be used for firing the test http requests towards the application. And therefore we have a second line in our beforeAll method to get that HttpClient bean from the application context.
client = embeddedServer.getApplicationContext().getBean(HttpClient.class);
Server URL to be supplied – It is not possible to test an endpoint like get("/api/todos") like we did in Quarkus. The server URL has to be supplied to the http client always like we did in the spring boot implementation.
Resource and Repository – Resource implementation is almost the same as in Quarkus or Spring Boot, except that micronaut comes with JAXRS type annotations inbuilt.
For Repository – Unfortunately I couldn’t find a Micronaut solution in place of Quarkus Panache or Spring Boot Data Mongodb. I could find a solution available for relational databases (Micronaut Data) but nothing for non-relational databases.
So the repository implementation needs a lot of lines of code which makes it unattractive solution. Here is my repository class, feel free to share if you have found better solutions
@Bean
public class TodoRepository {
private final MongoClient mongoClient;
public TodoRepository(MongoClient mongoClient) {
this.mongoClient = mongoClient;
}
public List<Todo> getAll() {
MongoCollection<Todo> collection = JacksonMongoCollection.builder().build(mongoClient,
"todos-app", "todos", Todo.class, UuidRepresentation.STANDARD);
return StreamSupport.stream(collection.find().spliterator(), false)
.collect(Collectors.toList());
}
public void save(Todo todo) {
MongoCollection<Todo> collection = JacksonMongoCollection.builder().build(mongoClient,
"todos-app", "todos", Todo.class, UuidRepresentation.STANDARD);
collection.insertOne(todo);
}
}
You may try the other CRUD operations – update and delete yourself. I believe it would be more or less the same as the current implementation. Please feel free to share if you have found any major differences worth mentionable here.
Micronaut/Kotlin – Mean time, I have done the same exercise using Micronaut/Kotlin as well. But I’m not really satisfied with the results. First of all the tests of the generated code itself was broken (or not working well with the IDE) which slowed down my experiments. Here is the repository for the same. I will write another post for the kotlin implementations for both Quarkus and Micronaut after more detailed experiments.
I hope you have enjoyed the read. Please follow me on twitter to receive my latest experiments.