DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • The Most Popular Technologies for Java Microservices Right Now
  • Frequently Used Annotations in Spring Boot Applications
  • Java and MongoDB Integration: A CRUD Tutorial [Video Tutorial]
  • Providing Enum Consistency Between Application and Data

Trending

  • Transforming AI-Driven Data Analytics with DeepSeek: A New Era of Intelligent Insights
  • Kubeflow: Driving Scalable and Intelligent Machine Learning Systems
  • AI-Based Threat Detection in Cloud Security
  • Revolutionizing Financial Monitoring: Building a Team Dashboard With OpenObserve
  1. DZone
  2. Testing, Deployment, and Maintenance
  3. Testing, Tools, and Frameworks
  4. Micronaut With Relation Database and...Tests

Micronaut With Relation Database and...Tests

This article is a Micronauts HTTP server with a relation database persistence layer. We created this focussing on the tests at the controller layer.

By 
Manel Naspreda user avatar
Manel Naspreda
·
Nov. 04, 20 · Opinion
Likes (4)
Comment
Save
Tweet
Share
10.9K Views

Join the DZone community and get the full member experience.

Join For Free

Introduction

I am writing this article as I was starting to read about Micronauts.

I have been working with spring-boot over the past years, which seems very complete from my point of view. But, as I was hearing about the Micronauts project I wanted to go through and compare what I would find there with my current experience.

All the code described here is committed on my GitHub:

https://github.com/naspredam/rest-micronaut-reactive-java-users

What Is Micronaut?

You can find a lot of literature on Micronauts, and have a lot of features:

https://micronaut.io/documentation.html

To simplify, as I am not pretending to go through all Micronauts offers... As a basic, this framework aims to:

  • Can be written in: Java (chosen on this article), Kotlin and Groovy
  • Compile-time AOP and dependency injection (same mind-set as spring)
  • Reactive HTTP client and server
  • A suite of cloud-native features

What I will focus on this article is just an HTTP server with a relational database. We are going to do a CRD service where we have the domain object UserData, where we want (the U is not implemented on the code, but it is really straightforward):

  • GET: all of them
  • POST: create one
  • DELETE: one
  • GET: get one by id

Create the Service

When I started to create the project, I saw that it was a command-line with SDK. It seems to be the first option of the documentation, and the command line is very complete. However, there is also the link:

https://micronaut.io/launch/

Which is pretty much as spring does with https://start.spring.io/.

I liked more doing on the link, but you can create the service using command line, up to you.

What We Were Looking for

What I am going to create is a service that it is very common, plain:

  • Java language
  • API HTTP
  • JPA (Java Persistent API)
  • MySQL (relational database)
  • Gradle

This was quite straightforward to do, as with the URL above we have it all, just need select application and then on dependencies look for:

  • Micronaut-hibernate-JPA
  • MySQL

and add them on your project. Then finally you will download it in a zip file.

Implementation

When we go to implement we have to:

  • Create the code to make our application work (obviously...)
  • Create tests to make sure that the application will continue working

It is quite common that the second point is less obvious :) However, when doing TDD the order of the steps are the other way around as the test should be the first step.

Let's Start With Coding?

What I am going to present this is to create the service based on the tests. As it would be boring to do baby steps on the article, what I have organized is based on milestones, where I am going to present the steps that has some major change.

There are three milestones, where the third one we would have the service running and returning something, based on the database.

Milestone 1 — Doing a Dummy Endpoint

If we try with the test, we can do the following, thinking to start with and endpoint GET to get all users:

Java
 




x


 
1
package com.service.micronaut.users;
2

          
3
import io.micronaut.core.type.Argument;
4
import io.micronaut.http.client.RxHttpClient;
5
import io.micronaut.http.client.annotation.Client;
6
import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
7
import org.junit.jupiter.api.Test;
8

          
9
import javax.inject.Inject;
10
import java.util.List;
11

          
12
import static io.micronaut.http.HttpRequest.*;
13
import static org.assertj.core.api.Assertions.assertThat;
14

          
15
@MicronautTest
16
public class UserControllerTest {
17

          
18
    @Inject
19
    @Client("/")
20
    private RxHttpClient client;
21

          
22
    @Test
23
    public void shouldReturnEmptyListWhenNoInformationPersisted() {
24
        HttpResponse<List<UserData>> usersResponse = client.toBlocking()
25
                .exchange(GET("/users"), Argument.listOf(UserData.class));
26

          
27
        assertThat(usersResponse.code()).isEqualTo(200);
28
        assertThat(usersResponse.body()).isEmpty();
29
    }
30
}
31

          



Here we can see that we have:

  • @MicronautTest: Which acts as @SpringBootTest starting the application on test mode
  • RxHttpClient: A real client that calls the endpoint, for later to validate the response
  • I am using here a third party library for assertions: Assertj. This is really optional...I could use direclty JUnit itself. You can see on github about the dependency used.

The DTO corresponds to:

Java
 




x


 
1
package com.service.micronaut.users;
2

          
3
import lombok.*;
4

          
5
@Getter
6
@Builder
8
@Table(name = "users")
7
@EqualsAndHashCode
8
@NoArgsConstructor
9
@AllArgsConstructor
10
public class UserData {
11

          
12
    private Long id;
13

          
14
    private String firstName;
15

          
16
    private String lastName;
17

          
18
    private String phone;
19
}



We used Lombok for avoiding to write too much code, even though we have quite a lot of annotations... this is also optional. This object will become our JPA entity (I am not going to separate presentation and persistence layers, to make this simpler).

With that, the minimum code we need is:

Java
 




xxxxxxxxxx
1
15


 
1
package com.service.micronaut.users;
2

          
3
import io.micronaut.http.annotation.*;
4

          
5
import java.util.List;
6

          
7
@Controller("/users")
8
public class UserController {
9

          
10
    @Get
11
    public List<UserData> getAllUsers() {
12
        return List.of();
13
    }
14

          
15
}



Milestone 2 — Create Repository (Controller Dependency)

Now, I would like to go to the next step when we have a dummy repository, so we will add a dependency to the controller (the service architecture will be just controller/repository layers to have a very simple solution):

Java
 




xxxxxxxxxx
1
17


 
1
package com.service.micronaut.users;
2

          
3
import io.micronaut.transaction.annotation.ReadOnly;
4

          
5
import javax.inject.Singleton;
6
import java.util.List;
7

          
8
@Singleton
9
public class UserRepository  {
10
    
11
    @ReadOnly
12
    public List<UserData> findAll() {
13
        return List.of();
14
    }
15

          
16
}
17

          



Now the controller will need to have:

Java
 




x


 
1
package com.service.micronaut.users;
2

          
3
import io.micronaut.http.annotation.*;
4

          
5
import java.util.List;
6

          
7
@Controller("/users")
8
public class UserController {
9

          
10
    private final UserRepository userRepository;
11

          
12
    public UserController(UserRepository userRepository) {
13
        this.userRepository = userRepository;
14
    }
15

          
16
    @Get
17
    public List<UserData> getAllUsers() {
18
        return userRepository.findAll();
19
    }
20

          
21
}
22

          



We really not done much, but we added a dependency, and now I have to ways proceed with test:

  • Unit test
  • Integration test

As the repository is dummy, we can focus on the unit test, later on when we have database we will focus on the integration tests.  So that, at a unit level, we should mock the final class dependency behaviour:

Java
 




x


 
1
package com.service.micronaut.users;
2

          
3
import io.micronaut.core.type.Argument;
4
import io.micronaut.http.HttpResponse;
5
import io.micronaut.http.client.RxHttpClient;
6
import io.micronaut.http.client.annotation.Client;
7
import io.micronaut.test.annotation.MockBean;
8
import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
9
import org.junit.jupiter.api.Test;
10
import org.mockito.Mockito;
11

          
12
import javax.inject.Inject;
13
import java.util.List;
14

          
15
import static io.micronaut.http.HttpRequest.*;
16
import static org.assertj.core.api.Assertions.assertThat;
17

          
18
@MicronautTest
19
public class UserControllerTest {
20

          
21
    @Inject
22
    @Client("/")
23
    private RxHttpClient client;
24

          
25
    @Inject
26
    private UserRepository userRepository;
27

          
28
    @MockBean(UserRepository.class)
29
    public UserRepository mockUserRepository() {
30
        return Mockito.mock(UserRepository.class);
31
    }
32

          
33
    @Test
34
    public void shouldReturnEmptyListWhenNoInformationPersisted() {
35
        Mockito.when(userRepository.findAll()).thenReturn(List.of());
36

          
37
        HttpResponse<List<UserData>> usersResponse = client.toBlocking()
38
                .exchange(GET("/users"), Argument.listOf(UserData.class));
39

          
40
        assertThat(usersResponse.code()).isEqualTo(200);
41
        assertThat(usersResponse.body()).isEmpty();
42
        Mockito.verify(userRepository).findAll();
43
    }
44
}
45

          



Here we have the Micronauts annotation @MockBean. This annotation enables us to mock the implementation of the dependency.

It is interesting that on spring-boot there is the same annotation, however there it automatically creates a Mockito instance of the tagged dependency. Here, on the other hand, it is more flexible, as we could create a new fresh instance implementation, not needing any third-party dependency. On this approach, it seems needed to have an interface, then we could create the new instance based on the interface.

This was a very common practice on spring, some time ago, on the projects that I have been working, it seems that the team considers this interfaces more an over-engineering, not seeing much value on this. Then I decided to create the Mockito mock instance, which it is what I am used to, and mockito give me some methods to have different behaviours per test and also verifiers. Again this is nor mandatory, but my choose.

There is another options to use @Replaces, however the @MockBean, seemed more close from what I am used to.


Milestone 3 — Add Entity Manager

On this milestone, we are going to create the real repository (I am not going to unit test this class, and this will be covered with the integration test, on a real project I would do integration test on this class to have the tests against a real database).

What we have to do is to add the EntityManager on our repository:

Java
 




xxxxxxxxxx
1
25


 
1
package com.service.micronaut.users;
2

          
3
import io.micronaut.transaction.annotation.ReadOnly;
4

          
5
import javax.inject.Singleton;
6
import javax.persistence.EntityManager;
7
import java.util.List;
8

          
9
@Singleton
10
public class UserRepository  {
11

          
12
    private final EntityManager entityManager;
13

          
14
    public UserRepository(EntityManager entityManager) {
15
        this.entityManager = entityManager;
16
    }
17

          
18
    @ReadOnly
19
    public List<UserData> findAll() {
20
        return entityManager.createQuery("select u from UserData u", UserData.class)
21
                .getResultList();
22
    }
23

          
24
}
25

          



Here I am going directly to the JPA EntityManager, however, you could define other third party libraries, such as queydsl.

At this point, the unit test we created on the controller stays as it is. On the other hand, we can create an integration test of the controller, which will be from the API call to the database. We are going to use h2 for testing. To do so, we have to add the h2 dependency on our Gradle:

Groovy
 




xxxxxxxxxx
1


 
1
    testRuntimeOnly("com.h2database:h2")



Then on the application yml (I create a brand new on the test/resources, you could use also application-test.yml):

YAML
 




xxxxxxxxxx
1
13


 
1
datasources:
2
  default:
3
    url: jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
4
    driverClassName: org.h2.Driver
5
    username: sa
6
    password: ''
7

          
8
jpa:
9
  default:
10
    properties:
11
      hibernate:
12
        hbm2ddl:
13
          auto: update



And we have to add the column names on the data object:

Java
 




xxxxxxxxxx
1
29


 
1
package com.service.micronaut.users;
2

          
3
import lombok.*;
4

          
5
import javax.persistence.*;
6

          
7
@Getter
8
@Entity
9
@Builder
10
@Table(name = "users")
11
@EqualsAndHashCode
12
@NoArgsConstructor
13
@AllArgsConstructor
14
public class UserData {
15

          
16
    @Id
17
    @GeneratedValue(strategy = GenerationType.IDENTITY)
18
    private Long id;
19

          
20
    @Column(name = "first_name")
21
    private String firstName;
22

          
23
    @Column(name = "last_name")
24
    private String lastName;
25

          
26
    @Column(name = "phone")
27
    private String phone;
28
}
29

          



With that, we are ready for our integration test:

Java
 




xxxxxxxxxx
1
52


 
1
package com.service.micronaut.users;
2

          
3
import com.github.javafaker.Faker;
4
import io.micronaut.core.type.Argument;
5
import io.micronaut.http.client.RxHttpClient;
6
import io.micronaut.http.client.annotation.Client;
7
import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
8
import org.junit.jupiter.api.Test;
9

          
10
import javax.inject.Inject;
11
import javax.persistence.EntityManager;
12

          
13
import java.util.List;
14

          
15
import static io.micronaut.http.HttpRequest.*;
16
import static org.assertj.core.api.Assertions.assertThat;
17

          
18
@MicronautTest
19
public class UserControllerIntegrationTest {
20

          
21
    private static final Faker FAKER = new Faker();
22

          
23
    @Inject
24
    @Client("/")
25
    private RxHttpClient client;
26

          
27
    @Inject
28
    private EntityManager entityManager;
29

          
30
    @Test
31
    public void shouldReturnTheUserPersisted() {
32
        UserData userData1 = UserData.builder()
33
                .firstName(FAKER.name().firstName())
34
                .lastName(FAKER.name().lastName())
35
                .phone(FAKER.phoneNumber().phoneNumber())
36
                .build();
37
        UserData userData2 = UserData.builder()
38
                .firstName(FAKER.name().firstName())
39
                .lastName(FAKER.name().lastName())
40
                .phone(FAKER.phoneNumber().phoneNumber())
41
                .build();
42
        entityManager.persist(userData1);
43
        entityManager.persist(userData2);
44
        entityManager.getTransaction().commit();
45

          
46
        List<UserData> usersResponse = client.toBlocking()
47
                .retrieve(GET("/users"), Argument.listOf(UserData.class));
48

          
49
        assertThat(usersResponse).hasSize(2)
50
            .containsExactlyInAnyOrder(userData1, userData2);
51
    }
52
}



On test code, we do not have anything new from micronaut. It was introduced just java faker, which it is totally optional, to have more randomised test data.

Here the only pain point, I faced, is the need to commit the transaction. From my point of view, this is something that is better handled, as you do not have to commit anything, all stays on the same transaction. However, on this integration test the RxHttpClient is opening another thread, and when the HTTP request reaches the repository, it is in a different transaction as we are inserting the values... then if we do not commit, the result will be always an empty list.

You will find on the documentation that the annotation @MicronautTest rollback after every test. However, when we have to commit, there is nothing to rollback...Then we may have the h2 dirty with data from one test to the others.

Milestone 4 — Add All Wanted Operations

The other methods GET/POST/DELETE is really straightforward. The only thing that I may highlight is that on the non-read-only queries we have to use the annotation:

Java
 




xxxxxxxxxx
1


 
1
@TransactionalAdvice



But this is very organic from milestone 3.

All final code can be found at https://github.com/naspredam/rest-micronaut-reactive-java-users/

Conclusions

To sum up, I did not fall in love with this framework.

It seems to me that spring-boot is a little more advanced for development needs on API HTTP with JPA server point of view and with its tests.

Here, I list some points that I feel about Micronauts, on this experience:

  • I feel that spring is much better/stronger documented
  • I just saw @MicronautTest annotation for testing on Micronauts. In spring-boot I have more options for specific situations, which I feel this useful
  • The commit on the transaction to be able to test the endpoint on a integration test... this is something that I could live with, but it seems tricky to have to manually clean up h2... I personally find spring-boot more elegant way to deal with this
  • @MockBean annotation is kind of weird to have to create a method... I feel that spring-boot if more organic.
  • Micronaut does not offer a simple way to deal with queries, as spring-boot jpa does with JpaRepositories, which it is something that I missed
  • Spring offers the MockMvc, which helps to assert the responses, but I also know that we could use the library rest assured, to cover a similar approach.

If I have to decide to choose between those two, it is very likely that I would go to spring-boot.

However as a future step, I would like to see what is the performance of this framework, as this could be an important point another aspect, that was not addressed on this article.

Also, just say that this article is a very concrete example, there is muuuuuch more to see from this framework.

Hope this is helpful.

All the best!

Database Relational database unit test Spring Boot Java (programming language) Dependency injection integration test Integration Milestone (project management) application

Opinions expressed by DZone contributors are their own.

Related

  • The Most Popular Technologies for Java Microservices Right Now
  • Frequently Used Annotations in Spring Boot Applications
  • Java and MongoDB Integration: A CRUD Tutorial [Video Tutorial]
  • Providing Enum Consistency Between Application and Data

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!