Micronaut with Graal native image example.

As we have seen in the last couple of articles on how to create simple Micronauts application development and dockerizing it. In this article, we are gone exploring Helloworld Graal micronaut application.

Here is the definition from Wikipedia. If you are crossing this article that means you are familiar with either of the topic.

GraalVM is a Java VM and JDK based on HotSpot/OpenJDK, implemented in Java. It supports additional programming languages and execution modes, like an ahead-of-time compilation of Java applications for fast startup and low memory footprint. The first production-ready version, GraalVM 19.0, was released in May 2019.

Let us start coding and simultaneously enjoy the topic.

Create a micronaut application using CLI:

$ mn create-app helloworld-graal --features=graal-native-image

The default option is not available to add Graal support we have to use this option  — features=graal-native-image.

If you are using Java or Kotlin and IntelliJ IDEA make sure you have enabled annotation processing.

Now let us create one simple POJO class to hold Play name to make it simple.

import io.micronaut.core.annotation.Introspected;

@Introspected
public class Play{

    private String name;

    public Play(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

@Introspected annotation is used to generate BeanIntrospection metadata at compilation time. This information is using the render the POJO as JSON using Jackson without using reflection.

Now let us create the Singelton Service class and return play name randomly.

(Note:- The play names are Marathi Play names of famous Sri Pu la Deshpande)

import javax.inject.Singleton;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

@Singleton
public class PlayService {
// create list of plays
    private static final List<Play> PLAYS = Arrays.asList(
            new Play("Tujhe Ahe Tujpashi"),
            new Play("Sundar Mee Honar"),
            new Play("Tee Phularani"),
            new Play("Teen Paishacha Tamasha"),
            new Play("Ek Jhunj Varyashi")
    );
 // to choose random play from PLAYS list
    public Play randomPlay() {
        return PLAYS.get(new Random().nextInt(PLAYS.size()));
    }
}

Now we need a controller to serve the request of random play name from service class.

import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;

@Controller
public class PlayController {

    private final PlayService playService;

    public PlayController(PlayService playService) {
        this.playService = playService;
    }

    @Get("/randomplay")
    public Play randomPlay() {
        return playService.randomPlay();
    }
}

Created controller and injected service object using constructor injection and mapping of GET method using @Get(“/randomplay”).

Now our application is ready you can test by executing below command.

$ ./gradlew run

http://localhost:8080/randomplay

JSON output 

{

 name: “Tee Phularani”

}

Let us create a Graal native image.

Micronaut only supported in Java or Kotlin for graal native-image.

While creating a project we have added — features=graal-native-image this is adding three important features. 

  1. svm(Substrate VM) and graal dependencies in build.gradle.
compileOnly "org.graalvm.nativeimage:svm"
annotationProcessor "io.micronaut:micronaut-graal"

2. A Dockerfile which can be used to construct the native image executing docker-build.sh

3. A native-image.properties file in the resource directory.

Args = -H:IncludeResources=logback.xml|application.yml|bootstrap.yml \
       -H:Name=helloworld-graal \
       -H:Class=helloworld.graal.Application

This is very easy for developer to create a native image inside docker. Fire below two commands: 

$ ./gradlew assemble
$ ./docker-build.sh

Once image is ready we can create a container to verify our understanding. 

$ docker run -p 8080:8080 helloworld-graal

To test the application you can use curl with time:

$ time curl localhost:8080/randomplay

This is for now. You can check the time difference with native image executable and docker with a native image. 

Source code download or clone from github: https://github.com/maheshwarLigade/micronaut-examples/tree/master/helloworld-graal

Dockerise Micronaut application.

Micronauts is a java framework to develop a cloud-native microservices application easily and seamlessly. If you don’t know about Micronaut Please go through below two articles. 

In this article, we are exploring a micronaut framework and How to dockerize it. 

Let us create a small micronaut REST service application and try to dockerize it.

Micronaut provides a CLI option to create an application easily.

$ mn create-app helloworld

This will scaffold a new Gradle project. If you prefer Maven, add a --build maven parameter. If you want to create a new Groovy or Kotlin project, add a --lang parameter.

$ mn create-app --lang groovy helloworld-groovy
$ mn create-app --lang kotlin helloworld-kotlin

These options depend on you, which language are you comfortable with.

These options depend on you, which language are you comfortable with. 

Once the project is ready we can import that in your favorite editor. I am using IntelliJ.

We are using already created Hello world app, source code is available at below location you can clone

https://github.com/maheshwarLigade/micronaut-examples/tree/master/helloworld

By default, micronaut app can create Docker file for you and docker file you can locate on current directory of your project <appname>/Docker 

e.g helloworld/Docker

The Default content of Docker file:

FROM adoptopenjdk/openjdk13-openj9:jdk-13.0.2_8_openj9-0.18.0-alpine-slim
COPY build/libs/helloworld-*-all.jar helloworld.jar
EXPOSE 8080
CMD ["java", "-Dcom.sun.management.jmxremote", "-Xmx128m", "-XX:+IdleTuningGcOnIdle", "-Xtune:virtualized", "-jar", "helloworld.jar"]

If you are familiar with docker then fine if not you can explore below article to understand docker.

https://www.techwasti.com/demystify-docker-container-technology-9a8e1ec3968b/

Micronaut create docker file with alpine-slim 

and JDK image which is used here is unofficial.

This repo provides Unofficial AdoptOpenJDK Docker Images,

Reference:- https://hub.docker.com/r/adoptopenjdk/openjdk13-openj9

Thrid line to copy the generated jar(helloworld.jar) file and the expose default port as 8080. Last line to launch the jar file.

For this example, I am using Gradle as a build tool

$ cd helloworld
$ ./gradlew run

To test whether code is working fine or not. (curl http://localhost:8080/hello)

Now build a Docker image from the docker file for that fire below command. 

To run the application with IntelliJ IDEA, you need to enable annotation processing:

  1. open Settings → Build → Execution → Deployment → Compiler →Annotation Processors
  2. Set the checkbox Enable annotation processing

As we know micronaut CLI generates a Dockerfile by default, making it easy to package your application for a container environment such as Kubernetes.

$ docker build . -t hello-world-ex

Fire above command to create a docker image. -t 1.0.0 indicates the tag for this image. Now our image is ready to make a container from its fire below command. 

$ docker run --rm -p 8080:8080 hello-world-ex

As we have exposed 8080 port in docker file. We are doing port mapping to an external system.

to verify the docker image fire below command.

$ curl http://localhost:8080/hello

In this article, we have seen dockerizing micronaut apps. We have created helloworld application and created a docker image using the existing Docker file. You can edit the docker file and optimize it as per your requirement. 

Spring Boot Firebase CRUD

In this article, we show How to build a CRUD application using Firebase and Spring boot.

Create a Firebase project in the Firebase console:

https://console.firebase.google.com/

Hit the https://console.firebase.google.com and sign up for an account.

Click the “Add Project” button from the project overview page.

Type “Firebase DB for Spring Boot” in the “Project name” field.

Click the “CREATE PROJECT” button.

Now we have created a project on Firebase, now let us add firebase to our spring boot app.

Add Firebase to your web app:

You can find your Realtime Database URL in the Database tab (DEVELOP → Database → Realtime Database → Start in test Mode ) in the Firebase console. It will be in the form of https://<databaseName>.firebaseio.com.

Create Firebase in test mode this is not useful for Prod development but for our this article we will use it in test mode which is available publicly. 

Your Database URL should look like this https://<Projectname XYZ>.firebaseio.com/

Our data is ready but still, we need a service account 

Go and click on Project settings → Service Accounts → Choose Language as Java. to copy code snippet

and Download JSON file as well by clicking on “Generate new private key”

We will also grab the admin SDK configuration snippet for java.

Then go to https://start.spring.io/ and create a project, Once the project added then open the pom.xml file and add below dependency.

<dependency>
    <groupId>com.google.firebase</groupId>
    <artifactId>firebase-admin</artifactId>
    <version>6.11.0</version>
 </dependency>

Now everything is ready to Let us initialize Firebase Database.

import com.google.auth.oauth2.GoogleCredentials;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.FileInputStream;

@Service
public class FBInitialize {

    @PostConstruct
    public void initialize() {
        try {
            FileInputStream serviceAccount =
                    new FileInputStream("./serviceaccount.json");

            FirebaseOptions options = new FirebaseOptions.Builder()
                    .setCredentials(GoogleCredentials.fromStream(serviceAccount))
                    .setDatabaseUrl("https://chatapp-e6e15.firebaseio.com")
                    .build();

            FirebaseApp.initializeApp(options);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

I am using the existing Firebase Database.

@Service and @PostConstruct these are the two annotations from Spring Boot. 

First-line reads the configurations from the JSON file and then initializes the connection for the specified database. 

Now firebase connection is initialized then let us create CRUD operations.

Create a POJO class as a Patient

public class Patient {

    private String name;

    private int age;

    private String city;


    public Patient(String name, int age, String city) {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }
}

Create Service class

import com.google.api.core.ApiFuture;
import com.google.cloud.firestore.DocumentReference;
import com.google.cloud.firestore.DocumentSnapshot;
import com.google.cloud.firestore.Firestore;
import com.google.cloud.firestore.WriteResult;
import com.google.firebase.cloud.FirestoreClient;
import org.springframework.stereotype.Service;

import java.util.concurrent.ExecutionException;

//CRUD operations
@Service
public class PatientService {

    public static final String COL_NAME="users";

    public String savePatientDetails(Patient patient) throws InterruptedException, ExecutionException {
        Firestore dbFirestore = FirestoreClient.getFirestore();
        ApiFuture<WriteResult> collectionsApiFuture = dbFirestore.collection(COL_NAME).document(patient.getName()).set(patient);
        return collectionsApiFuture.get().getUpdateTime().toString();
    }

    public Patient getPatientDetails(String name) throws InterruptedException, ExecutionException {
        Firestore dbFirestore = FirestoreClient.getFirestore();
        DocumentReference documentReference = dbFirestore.collection(COL_NAME).document(name);
        ApiFuture<DocumentSnapshot> future = documentReference.get();

        DocumentSnapshot document = future.get();

        Patient patient = null;

        if(document.exists()) {
            patient = document.toObject(Patient.class);
            return patient;
        }else {
            return null;
        }
    }

    public String updatePatientDetails(Patient person) throws InterruptedException, ExecutionException {
        Firestore dbFirestore = FirestoreClient.getFirestore();
        ApiFuture<WriteResult> collectionsApiFuture = dbFirestore.collection(COL_NAME).document(person.getName()).set(person);
        return collectionsApiFuture.get().getUpdateTime().toString();
    }

    public String deletePatient(String name) {
        Firestore dbFirestore = FirestoreClient.getFirestore();
        ApiFuture<WriteResult> writeResult = dbFirestore.collection(COL_NAME).document(name).delete();
        return "Document with Patient ID "+name+" has been deleted";
    }

}

Now we are ready with CRUD operation let us develop the REST Controller which will help us in interaction with this service layer.

Note:- You have to enable Cloud FireStore API.

Now we just need to create Controller which can handle REST request.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.concurrent.ExecutionException;

@RestController
public class PatientController {

    @Autowired
    PatientService patientService;

    @GetMapping("/getPatientDetails")
    public Patient getPatient(@RequestParam String name ) throws InterruptedException, ExecutionException{
        return patientService.getPatientDetails(name);
    }

    @PostMapping("/createPatient")
    public String createPatient(@RequestBody Patient patient ) throws InterruptedException, ExecutionException {
        return patientService.savePatientDetails(patient);
    }

    @PutMapping("/updatePatient")
    public String updatePatient(@RequestBody Patient patient  ) throws InterruptedException, ExecutionException {
        return patientService.updatePatientDetails(patient);
    }

    @DeleteMapping("/deletePatient")
    public String deletePatient(@RequestParam String name){
        return patientService.deletePatient(name);
    }
}

Now coding is done try by yourself and let’s know.

Spring Tools 4 for Visual Studio Code.

Visual studio code is the most popular, open-source and lightweight editor in the market. Spring boot has also another popular and powerful Framework in the Java ecosystem. It gains popularity because of simplicity and bootstrap the development. Spring boot is very handy to develop microservice-based applications and also support for cloud-native development. 

This article is for those who want to leverage their VS code editor to develop spring framework application. Spring comes up with tools to support the development of spring framework based application in VSCode.

Spring really provides the flexibility to a developer, for spring developer you don’t need any special editor, IDE or OS or any tool suite as well. 

Spring is nature’s way of saying, ‘Let’s Party!

As per the above quotes spring framework really saying developer don’t worry let’s do a party I will take care of everything. 

If you have hit this article that means you are familiar with Visual Studio code editor. You can download visual studio code if you haven’t by using the below link:

https://code.visualstudio.com/

Configure the Spring boot with Visual Studio Code

After installation of vs code editor on your local system. Now we have considered below points to configure Spring boot with VS code.

I will assume you have done below configurations

  1. VS code installation.
  2. Java extension for VS Code.
  3. Kotlin extension if you want to develop a spring boot app using kotlin.

If above everything is done then go and open VS Code and go to extension and search for “spring”. You will able to something like the below image in your vscode editor.

Click and install “Spring Boot Extension Pack” Once the installation is done reload the VS code.

Spring Boot Extension Pack is acollection of extensions for developing and deploying Spring Boot Application.

  1. Spring boot tools.
  2. Spring Initializr Java Support.
  3. Spring Boot Dashboard.

Spring Boot Tools:

VSCode extension and Language Server providing support for working with Spring Boot application.properties, application.yml and .java files.

Spring Initializr Java Support:

Spring Initializr is a lightweight extension to quickly generate a Spring Boot project in Visual Studio Code (VS Code). It helps you to customize your projects with configurations and manage Spring Boot dependencies.

Spring Boot Dashboard:

Spring Boot Dashboard is a lightweight extension in Visual Studio Code (VS Code). With an explorer in the side bar, you can view and manage all available Spring Boot projects in your workspace. It also supports the features to quickly start, stop or debug a Spring Boot project.

Feature List

  • View Spring Boot apps in workspace
  • Start / Stop a Spring Boot app
  • Debug a Spring Boot app
  • Open a Spring Boot app in the browser
  • Generate a Maven/Gradle Spring Boot project
  • Customize configurations for a new project (language, group id, artifact id, boot version, and dependencies)
  • Search for dependencies
  • Quickstart with last settings
  • Edit Spring Boot dependencies of an existing Spring Boot project

Extention pack contains:

  1. IDE Java tooling for developing and troubleshooting Spring Boot applications.
  2. It provides support for editing Cloud Foundry deployment manifest .yml files for Spring Boot application deployment.
  3. The Concourse CI Pipeline Editor provides support for setting up Concourse build pipeline for the Spring Boot application.
  4. It provides support for generating quickstart Spring Boot Java projects with Spring Initiailizr API.
  5. It provides an explorer in the sidebar where you can view all of a workspace’s spring boot projects conveniently in one place.

This is it for now up to installation.

Want to create an application using vscode, check the below video. 

Spring Boot in VS Code

Use of spring initializer 

  • Launch VS Code
  • Press Ctrl + Shift + P to open the command palette.
  • Type Spring Initializr to start generating a Maven or Gradle project.
  • Follow the wizard.
  • Right-click inside the pom.xml file and choose Edit starters for dependency refactoring. (Gradle project is not supported yet, PR is welcome for it.)

Shortcuts may change based on the Operating System.

More such Stories

Configuration as a Service: Spring Cloud Config – using kotlin.

Developing a microservice architecture with Java and Spring Boot is quite popular these days. In microservice architecture we hundreds of services and managing services for each service and for each profile which is a quite tedious task. In this article, we will demonstrate the Spring cloud config server using kotlin. 

Spring Boot provided a much-needed spark to the Spring projects.

Spring cloud-config provides a server and client-side support for externalizedconfiguration in a distributed system. With the Config Server, you have a central place to manage external properties for applications across all environments.

From the above diagram, you can easily predict that in distributed systems managing configuration as a central service is a bit tedious task and spring cloud config provide client, server architecture mechanism to manage the configuration easily. 

Let us go to the https://start.spring.io/

When we do any changes in any service we have to restart the services to apply the changes.

Let us create one git repo to manage our configuration and to achieve this we are creating one git repo.

So we will create “springbootclient” as one small spring boot microservice to take the username and that will read username from spring cloud config central configuration server system i.e git here.

We have created three different properties files for each of our different environments. 

  1. springbootclient.properties
  2. springbootclient-dev.properties
  3. springbootclient-prod.properties

https://github.com/maheshwarLigade/cloud-common-config-server

Here is our spring cloud config properties are available, you can clone or use directly this repository too.

Now as we have created spring config server application using spring starter let us download and import that project in your favorite IDE or editor. git repo here we used to store our configuration and spring cloud config server application is to serve those properties to the client.

Basically git is datastore, spring cloud config server is server application and there are multiple microservices are the clients which needs configurations.

Now our git as datastore is ready. In this repository, we have created one sample client application and the name of that app is springbootclient. In the future microservice article we will utilize the same spring cloud config as a configuration server.

Let us go and check the code base for the client app.

This is the sample application.properties file:

server.port=8888
logging.level.org.springframework.cloud.config=DEBUG
spring.cloud.config.server.git.uri=https://github.com/maheshwarLigade/cloud-common-config-server.git
spring.cloud.config.server.git.clone-on-start=true
spring.cloud.config.server.git.searchPaths=springbootclient

Sample Code for SpringCloudConfigServerexApplication.kt

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.cloud.config.server.EnableConfigServer

@SpringBootApplication
@EnableConfigServer
class SpringCloudConfigServerexApplication

fun main(args: Array<String>) {
   runApplication<SpringCloudConfigServerexApplication>(*args)
}

Now run and up the spring cloud-config server and check the below URL:

http://localhost:8888/springbootclient/dev/master

Spring Boot Client App:

Let us create one small microservice which will read configuration from spring cloud config server and serve that property value over REST end point.

Go to the https://start.spring.io/ and create spring boot client microservice using kotlin.

Sample POM.xml dependencies.

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
   <groupId>com.fasterxml.jackson.module</groupId>
   <artifactId>jackson-module-kotlin</artifactId>
</dependency>
<dependency>
   <groupId>org.jetbrains.kotlin</groupId>
   <artifactId>kotlin-reflect</artifactId>
</dependency>
<dependency>
   <groupId>org.jetbrains.kotlin</groupId>
   <artifactId>kotlin-stdlib-jdk8</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-config</artifactId>
</dependency>

Now check the SpringCloudClientAppApplication.kt code

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class SpringCloudClientAppApplication

fun main(args: Array<String>) {
    runApplication<SpringCloudClientAppApplication>(*args)
}

Now create one sample REST controller which is serving REST request. We want to check ” /whoami” this endpoint is returning which is the user based on active profile dev, prod, etc.

UserController.kt

import org.springframework.beans.factory.annotation.Value
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController


@RestController
class UserController {

    @Value("\${app.adminusername}")
    var username="Test"
//get request serving
    @GetMapping("/whoami")
    fun whoami() = "I am a  "+ username

}

Create a bootstrap.properties file where we will specify the spring cloud config server details, which is a git branch and what is active profile dev, local, prod, etc.

spring.application.name=springbootclient
spring.profiles.active=dev
spring.cloud.config.uri=http://localhost:8888
spring.cloud.config.fail-fast=true
spring.cloud.config.label=master

All properties are self exclamatory, what is the use of which one.

Once you hit this URL http://localhost:9080/whoami

Output:- I am a DevUser

Github source link:

Config Server: https://github.com/maheshwarLigade/cloud-common-config-server

Codebase: https://github.com/maheshwarLigade/spring-cloud-config-kotlin-ex

More such Stories

Spring Boot, MongoDB REST API using Kotlin.

As part of this article our focus to develop simple REST API using spring boot and MongoDB. 

Getting started with this is the Spring Initialiser tool: https://start.spring.io/

In this example, I am considering gradle as build tool and MongoDB as Database.

Download and import Project into your favorite editor, I prefer intellij,

Either you can install MongoDB on your local or you can use MongoDB hosted solution https://mlab.com/.

I am using mlab.com for this example.

Let us provide the MongoDB connection details in the application.properties

spring.data.mongodb.host=localhost #for now I kept localhost
spring.data.mongodb.port=27017
spring.data.mongodb.database=mongo-rest-api-kotlin-demo

Let us create entity class as Patient.

@Document
data class Patient (
        @Id
        val id: ObjectId = ObjectId.get(),
        val name: String,
        val description: String,
        val createdDate: LocalDateTime = LocalDateTime.now(),
        val modifiedDate: LocalDateTime = LocalDateTime.now()
)

@Document annotation rather than @Entity is used here for marking a class which objects we’d like to persist to the mongodb. 

@Id: is used for marking a field used for identification purposes. 

Also, we have provided some default values for the created date and modified date.

Let us create a repository interface.

import org.bson.types.ObjectId
import org.springframework.data.mongodb.repository.MongoRepository

interface PatientRepository : MongoRepository<Patient, String> {
    fun findOneById(id: ObjectId): Patient
    override fun deleteAll()

}

The repository interface is ready to use, we don’t have to write an implementation for it. This feature is provided by SpringData JPA. Also, MongoRepository interface provides all basic methods for CRUD operations. For now, we will consider only finOneById.

Now our backend is ready, let us write down the REST Controller which will serve our request efficiently.

@RestController
@RequestMapping("/patients")
class PatientController(
        private val patientsRepository: PatientRepository
) {

    @GetMapping
    fun getAllPatients(): ResponseEntity<List<Patient>> {
        val patients = patientsRepository.findAll()
        return ResponseEntity.ok(patients)
    }

    @GetMapping("/{id}")
    fun getOnePatient(@PathVariable("id") id: String): ResponseEntity<Patient> {
        val patient = patientsRepository.findOneById(ObjectId(id))
        return ResponseEntity.ok(patient)
    }
}

Now our basic Controller is ready, let us write some test cases.

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ExtendWith(SpringExtension::class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class PatientControllerIntTest @Autowired constructor(
        private val patientRepository: PatientRepository,
        private val restTemplate: TestRestTemplate
) {
    private val defaultPatientId = ObjectId.get()

    @LocalServerPort
    protected var port: Int = 0

    @BeforeEach
    fun setUp() {
        patientRepository.deleteAll()
    }


    private fun getRootUrl(): String? = "http://localhost:$port/patients"

    private fun saveOnePatient() = patientRepository.save(Patient(defaultPatientId, "Name", "Description"))

    @Test
    fun `should return all patients`() {
        saveOnePatient()

        val response = restTemplate.getForEntity(
                getRootUrl(),
                List::class.java
        )

        assertEquals(200, response.statusCode.value())
        assertNotNull(response.body)
        assertEquals(1, response.body?.size)
    }

    @Test
    fun `should return single patient by id`() {
        saveOnePatient()

        val response = restTemplate.getForEntity(
                getRootUrl() + "/$defaultPatientId",
                Patient::class.java
        )

        assertEquals(200, response.statusCode.value())
        assertNotNull(response.body)
        assertEquals(defaultPatientId, response.body?.id)
    }
}

Here we are using spring boot test to do integration testing also SpringBootTest.WebEnvironment.RANDOM_PORT is used here.

Note:

https://kotlinlang.org/docs/reference/coding-conventions.html#naming-rules

Please consider the naming convention while writing test cases for kotlin.

In JVM world similar conventions are well-known in Groovy and Scalaworld.

Always start with simple steps first, we will write down get operation first, try to fetch all Patient details.

Run the application and hit the http://localhost:8090/patients endpoint.

Let us create a POST request.

Create one simple request Object that will help us to create entity in mango world.

class PatientRequest(
        val name: String,
        val description: String
)

Here we will Pass patient names and descriptions about treatment. 

Now go to the REST Controller and handle a POST request.

@PostMapping
fun createPatient(@RequestBody request: PatientRequest): ResponseEntity<Patient> {
    val patient = patientsRepository.save(Patient(
            name = request.name,
            description = request.description
    ))
    return ResponseEntity(patient, HttpStatus.CREATED)
}

Let us create a method to create a PUT method to handle amendments in a document.

@PutMapping("/{id}")
fun updatePatient(@RequestBody request: PatientRequest, @PathVariable("id") id: String): ResponseEntity<Patient> {
    val patient = patientsRepository.findOneById(ObjectId(id))
    val updatedPatient = patientsRepository.save(Patient(
            id = patient.id,
            name = request.name,
            description = request.description,
            createdDate = patient.createdDate,
            modifiedDate = LocalDateTime.now()
    ))
    return ResponseEntity.ok(updatedPatient)
}

Test Method for an Update operation.

@Test
fun `should update existing patient`() {
    saveOnePatient()
    val patientRequest = preparePatientRequest()

    val updateResponse = restTemplate.exchange(
            getRootUrl() + "/$defaultPatientId",
            HttpMethod.PUT,
            HttpEntity(patientRequest, HttpHeaders()),
            Patient::class.java
    )
    val patientRequest = patientRepository.findOneById(defaultPatientId)

    assertEquals(200, updateResponse.statusCode.value())
    assertEquals(defaultPatientId, patientRequest.id)
    assertEquals(patientRequest.description, patientRequest.description)
    assertEquals(patientRequest.name, patientRequest.name)
}

Now our update operation is ready.

Let us Delete records using Delete operation.

As the deleted document won’t be included in the response, the 204 code will be returned.

@DeleteMapping("/{id}")
fun deletePatient(@PathVariable("id") id: String): ResponseEntity<Unit> {
    patientsRepository.deleteById(id)
    return ResponseEntity.noContent().build()
}

Test method which is straight forward to test delete method.

@Test
fun `should delete existing patient`() {
    saveOnePatient()

    val delete = restTemplate.exchange(
            getRootUrl() + "/$defaultPatientId",
            HttpMethod.DELETE,
            HttpEntity(null, HttpHeaders()),
            ResponseEntity::class.java
    )

    assertEquals(204, delete.statusCode.value())
    assertThrows(EmptyResultDataAccessException::class.java) { patientRepository.findOneById(defaultPatientId) }
}

Now our all CRUD operations are ready, run the application

This is for now Code is available on Github

https://github.com/maheshwarLigade/springboot-mongodb.restapi/tree/master

JUnit5: Parameterized Tests

As we studied in Junit5 in part1 and part2. Junit5 is very impressive in the extension model and architectural style, also the Junit5 Assumptions. The another best aspect about junit5 is compatibility with a lambda expression. In this section let us start looking for Junit5 parameterized tests.

The term parameter is often used to refer to the variable as found in the function definition, while argument refers to the actual input passed.

Maven Dependency:

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.4.2</version>
<scope>test</scope>
</dependency>

Gradle Dependency:

testCompile("org.junit.jupiter:junit-jupiter-params:5.4.2")

The basic difference while annotating the method instead of start by declaring a test method on @ParameterizedTest instead of @Test for a parameterised test. There are few scenarios where we want to pass values dynamically as method argument and a unit test that for this type of a scenario parameterized test cases are useful.

It looks below code is incomplete. From where this word value will come. how would JUnit know which arguments the parameter word should take? And indeed, Jupiter engine does not execute the test and instead throw a PreconditionViolationException.

@ParameterizedTest
void parameterizedTest(String word) {
 assertNotNull(word);
}
Configuration error: You must provide at least
one argument for this @ParameterizedTest

Let us start correcting the above exception.

@ParameterizedTest
@ValueSource(strings = {"JUnit5 ParamTest" , "Welcome"})
void withValueSource(String word) {
 assertNotNull(word);
}

Now above code will successfully get executed. This is just a simple use case but in real life project, you need more tools for that purpose you should know in detail of @ValueSource annotation.

ValueSource:

@ValueSource annotation is to provide a source of argument to the parameterized test method. The source can be anything like single value, an array of values, null source, CSV file, etc. As we have seen in the above example @ValueSource annotation, we can pass an array of literal values to the test method.

public class Strings {
     public static boolean isEmptyString(String str) {
         return str == null || str.trim().isEmpty();
     }
 }
// Test case for the above method could be 
@ParameterizedTest
@ValueSource(strings = {"", "  ","Non Empty"})
 void isEmptyStringReturnTrueForNullOrBlankStrings(String str) {
     assertTrue(Strings.isEmptyString(str));
 }

Limitations of value sources:

1. It only support the following data types.

2. We can pass only one argument to the test method each time.

3. We can not pass null as a argument to the test method.

  • short (with the shorts attribute)
  • byte (with the bytes attribute)
  • int (with the ints attribute)
  • long  (with the longs attribute)
  • float (with the floats attribute)
  • double (with the doubles attribute)
  • char (with the chars attribute)
  • java.lang.String (with the strings attribute)
  • java.lang.Class (with the classes attribute)

@NullSource and @EmptySource:

We can pass a single null value to a parameterized test method using @NullSource and its not for the primitive data types.

@EmptySource passes a single empty argument and you can use empty source for collection types and for array too.

In order to pass both null and empty values, we can use the composed @NullAndEmptySource annotation

@ParameterizedTest
@NullAndEmptySource
@ValueSource(strings = {” “, “\t”, “\n”})
void isEmptyStringReturnTrueForAllTypesOfBlankStrings(String input) {
assertTrue(Strings.isEmptyString(input));
}

EnumSource:

The name implies its self, if we want to test different values from an enumeration, we can use @EnumSource.

@ParameterizedTest
@EnumSource(WeekDay.class)
void getValueForADay_IsAlwaysBetweenOneAndSeven(WeekDay day) {
int dayNumber = day.getValue();
assertTrue(dayNumber >= 1 && dayNumber <= 7);
}

We can filter out a few days by using the names attribute of enum. @EnumSource annotation has option to select enum constant mode, you can either include and exclude too using EnumSource.Mode.EXCLUDE.

We can pass a string literal and regular expression both to the names attribute

Reference Document

CsvSource:

We need argument sources capable of passing multiple arguments. As we know @ValueSource and @EnumSource are only allowing one argument each time. In real life project we want to read row input values manipulate those and unit test those for that purpose @CsvSource

@ParameterizedTest
@CsvSource(value = {“juniT:junit”, “MaN:man”, “Java:java”}, delimiter = ‘:’)
void toLowerCaseValue(String input, String expected) {
String actualValue = input.toLowerCase();
assertEquals(expected, actualValue);
}

In the above example you have key value pair with colon as a delimiter, we can also pass CSV file as a resource argument too:

//CSV file
input,expected
Ram,RAM
tYpE,TYPE
Java,JAVA
koTliN,KOTLIN

@ParameterizedTest
@CsvFileSource(resources = “/testdata.csv”, numLinesToSkip = 1)
void toUpperCaseValueCSVFile(String input, String expected) {
String actualValue = input.toUpperCase();
assertEquals(expected, actualValue);
}

resources attribute represents the CSV file resources on the classpath and we can pass multiple CSV files too. Let us take few more examples

@ParameterizedTest
@CsvSource({
“2019-09-21, 2018-09-21”,
“null, 2018-08-15”,
“2017-04-01, null”
})
void shouldCreateValidDateRange(LocalDate startDate, LocalDate endDate) {
new DateRange(startDate, endDate);
}

@ParameterizedTest
@CsvSource({
“2019-09-21, 2017-09-21”,
“null, null”
})
void shouldNotCreateInvalidDateRange(LocalDate startDate, LocalDate endDate) {
assertThrows(IllegalArgumentException.class, () -> new DateRange(startDate, endDate));
}

When you are executing above programming you will end up getting exception.

org.junit.jupiter.api.extension.ParameterResolutionException: Error converting parameter at index 0: Failed to convert String “null” to type java.time.LocalDate

The null value isn’t accepted in @ValueSource or@CsvSource.

Method Source:

The @ValueSource and @EnumSource and pretty simple and has one limitation they won’t support complex types. MethodSource allows providing complex argument source. MehtodSource annotation takes the name of a method as an argument needs to match an existing method that returns Steam type.

@ParameterizedTest
@MethodSource(“wordsWithLength”)
void withMethodSource(String word, int length) { }

private static Stream wordsWithLength() {
return Stream.of(
Arguments.of(“JavaTesting”, 10),
Arguments.of(“JUnit 5”, 7));
}

When we won’t provide a name for the @MethodSource, JUnit will search for a source method with the same name as the parameterized test  method.

@ParameterizedTest
@MethodSource(“wordsWithLength”)
void wordsWithLength(String word, int length) { }

Custom Argument Provider:

As of now, we have covered inbuilt argument provider but in few scenarios, this doesn’t work for you then Junit provides custom argument provider. You can create your own source, argument provider. To achieve this we have to implement an interface called ArgumentsProvider.

public interface ArgumentsProvider {
Stream<? extends Arguments> provideArguments(
    ContainerExtensionContext context) throws Exception;
}

Example:

For this, we have just test with a custom empty String provider.

class EmptyStringsArgumentProvider implements ArgumentsProvider {

@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
    return Stream.of( 
      Arguments.of(""), 
      Arguments.of("   "),
      Arguments.of((String) null) 
    );
}
}

We can use this custom argument a provider using @ArgumentSource annotation.

@ParameterizedTest
@ArgumentsSource(EmptyStringsArgumentProvider.class)
void isEmptyStringsArgProvider(String input) {
assertTrue(Strings.isBlank(input));
}

Summery:

As part of this article, we have discussed Parameterised test cases and argument provider in bits and pieces and some level of custom argument provider using ArgumentsProvider interface and @ArgumentsSource.

There are different source provider from primitive to CSV and MethodSource provider. This is for now.

Junit5 Assumptions

Assumptions are used to run tests only if certain conditions are met. This is typically used for external conditions that are required for the test to execute properly, but which are not directly related to whatever is being unit tested.

If the assumeTrue() condition is true, then run the test, else aborting the test.

If the assumeFalse() condition is false, then run the test, else aborting the test.

The assumingThat() is much more flexible, it allows part of the code to run as a conditional test.

When the assumption is false, a TestAbortedException is thrown and the test is aborting execution.

@Test
void trueAssumption() {
    assumeTrue(6 > 2);
    assertEquals(6 + 2, 8);
}

@Test
void falseAssumption() {
    assumeFalse(4 < 1);
    assertEquals(4 + 2, 6);
}

@Test
void assumptionThat() {
    String str = "a simple string";
    assumingThat(
        str.equals("a simple string"),
        () -> assertEquals(3 + 2, 1)
    );
}

https://www.techwasti.com/junit5-tutorial-part-1/
https://www.techwasti.com/junit5-part2/

Junit5 tutorial: Part2

Before exploring this part, please read first part 1 is here.

Junit5 for beginners. In this tutorial let us make our hands dirty and have practical experience. If you are using Maven or Gradle for either of the build tool you can use dependency.

Maven dependency for Junit 5.0:

Add below dependency in pom.xml.

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.1.0</version>
<scope>test</scope>
</dependency>

Junit 5.0 with Gradle:

Add below dependency in build.gradle file. We can start by supplying the unit test platform to the build tool. Now we have specified test platform as Junit.

test {useJUnitPlatform()}

Now after the above steps, we need to provide Junit5 dependencies here is the difference between Junit4 and junit5. As we have discussed in previous article Junit5 is modular so we have three different modules and each one has a different purpose.

dependencies 
{
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.1'
}

In JUnit 5, though, the API is separated from the runtime, meaning two dependencies have to provide testImplementation and timeRuntimeOnly respectively.

The API is manifest with junit-jupiter-api. The runtime is junit-jupiter-engine for JUnit 5, and junit-vintage-engine for JUnit 3 or 4.

1. JUnit Jupiter:

In the first article, we have explored the difference between Junit4 and Juni5. There are new annotations introduced as part of this module. JUnit 5 new annotations in comparison to JUnit 4 are:

@Tag — Mark tags to test method or test classes for filtering tests.

@ExtendWith —Register custom extensions.

@Nested — Used to create nested test classes.

@TestFactory — Mark or denotes a method is a test factory for dynamic tests.

@BeforeEach — The method annotated with this annotation will be run before each test method in the test class. (Similar to Junit4 @Before )

@AfterEach — The method annotated with this annotation will be executed after each test method. (Similar to Junit4 @After )

@BeforeAll — The method annotated with this annotation will be executed before all test methods in the current class. (Similar to Junit4 @BeforeClass )

@AfterAll — The method annotated with this annotation will be executed after all test methods in the current class. (Similar to Junit4 @AfterClass )

@Disable — This is used to disable a test class or method (Similar to Junit4 @Ignore)

@DisplayName — This annotation defines a custom display name for a test class or a test method.

2. JUnit Vintage:

As part of this module, there is no new annotation has introduced but the purpose of this module is to supports running JUnit 3 and JUnit 4 based tests on the JUnit 5 platform.

Let us deep dive and do some coding:

@DisplayName and @Disabled:

As you can capture from the below code snipept you can DisplayName I have just given two flavors of one method. This way you can provide your own custom display name to identify test easily.

@DisplayName("Happy Scenario")
@Test
void testSingleSuccessTest() 
{
System.out.println("Happy Scenario");
}

@Test
@DisplayName("Failure scenario")
void testFailScenario() {
System.out.println("Failure scenario")
}

To disable test cases which implementation not yet completed or some other reason to skip that

@Test
@Disabled("Under constructution")
void testSomething() {
}

@BeforeAll and @BeforeEach :

BeforeAll is like a setup method this will get invoked once before all test methods in this test class and BeforeEach get invoked before each test method in this class.

@BeforeAll annotation must be, static and it’s run once before any test method is run.

@BeforeAll
static void setup() {
System.out.println("@BeforeAll: get executes once before all test methods in this class");
System.out.println("This is like setup for tests methods");
}

@BeforeEach
void init() {
System.out.println("@BeforeEach: get executes before each test method in this class");
System.out.println("initialisation before each test method");
}

@AfterEach and @AfterAll:

AfterEach gets invoked after each test method in this class and AfterAll get invoked after all test cases get invoked. Afterall like finalization task.

@AfterAll annotation must be, static and it’s run once after all test methods have been run.

@AfterEach
void tearDown() {
System.out.println("@AfterEach: get executed after each test method.");
}
@AfterAllstatic void finish() {
System.out.println("@AfterAll: get executed after all test methods.");
}

Assertions and Assumptions:

Assertions and assumptions are the base of unit testing. Junit5 taking full advantage of Java8 features such as lambda to make assertions simple and effective.

Assertions:

Junit5 assertions are part of a org.junit.jupiter.api.Assertions API and improvision have significantly as Java 8 is the base of Junit5 you can leverage all the features of Java8 primarily lambada expression. Assertions help in validating the expected output with the actual output of a test case.

@Test
void testLambdaExpression() {
assertTrue(Stream.of(4, 5, 9)
.stream()
.mapToInt(i -> i)
.sum() > 18, () -> "Sum should be greater than 18");
}

As you are aware of using lambda expression because of lambda expression

All JUnit Jupiter assertions are static methods

@Test
void testCase()
{
//Pass
Assertions.assertNotEquals(3, Calculator.add(2, 2));

//Fail
Assertions.assertNotEquals(4, Calculator.add(2, 2), "Calculator.add(2, 2) test failed");

//Fail
Supplier<String> messageSupplier = ()-> "Calculator.add(2, 2) test failed";
Assertions.assertNotEquals(4, Calculator.add(2, 2), messageSupplier);
}

With assertAll()which will report any failed assertions within the group with a MultipleFailuresError

Junit5 tutorial: Part-1

Junit5 tutorials for beginner.

Junit is the Java’s most popular unit testing library, recently has released a new version 5. JUnit 5 is a combination of several modules from three different sub-projects. 

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

JUnit is an open-source Unit Testing Framework for JAVA. It is useful for Java Developers to write and run unit tests. Erich Gamma and Kent Beck initially develop it.

JUnit 5 is an evolution of JUnit 4, and did some further improves the testing experience. As we are aware Junit5 is the major version release and its aims to adapt java 8 styles of coding and to be more robust and flexible than the previous releases. 

JUnit 5 was to completely rewrite JUnit 4

Advantages:- Below are the few advancements in this version over older once.

  1. The entire framework was contained in a single jar library.
  2. In JUnit 5, we get more granularity and can import only what is necessary.
  3. JUnit 5 makes good use of Java 8 styles of programming and features.
  4. JUnit 5 allows multiple runners to work simultaneously.
  5. The best thing about Junit5 is backward Compatibility for JUnit 4.

Note:- JUnit 5 requires Java 8 (or higher) at runtime.

Moving from Junit 4 to Junit5:-

In this section let us explore the motivation behind the Junit5.

  1. Junit4 was developed a decade ago, now context has bit changed and programming style too.
  2. Junit4 is not compatible with JDK8 and its new functional programming paradigm.
  3. Junit4 is not modular. A single jar is a dependency for everything. 
  4. Test discovery and execution are tightly coupled in Junit4.
  5. The most important thing nowadays developer not only want unit testing but also they want integration testing and system testing.

These are few reasons to rewrite junit5 from scratch using java8 and introduced some new features.

You can still execute Junit3 and Junit4 unit test cases using Vintage module in Junit5.

Architecture:-

Junit5 is modular architecture and the main three components are Platform, Jupiter and vintage.

Let us understand above this three module.

  1. Platform:- Platform, which serves as a foundation for launching testing frameworks on the JVM. It also provides an API to launch tests from either the console, IDEs, or build tools.
  2. Jupiter:- Jupiter is the combination of the new programming model and extension model for writing tests and extensions in JUnit 5. The name has been chosen from the 5th planet of our Solar System, which is also the largest one.
  3. Vintage:- Vintage in general something from the past. Vintage provides a test engine for running JUnit 3 and JUnit 4 based tests on the platform, ensuring the necessary backward compatibility.

As part of this tutorial, we are able to understand what is new Junit5 and what is it. In the next tutorial, we will explore more and we will take some examples.