top of page

Blogs

Spring boot 3 microservices with Spring Cloud Part-2

We have seen microservices development with basic components including eureka server, config server, distributed tracing using zipkin server and API testing of the same in Spring boot 3 microservices with Spring Cloud Part-1. Now let us jump into below topics :



Spring cloud API Gateway

consider a situation where we have a large number of microservices running in the cloud environment and we just want combined result of few services. There are 2 options here, either we need to call individual service and get the separate results and we need to write logic in the client side to get the segregated result. This approach seems inconvenient for the client as they end up making more network call and still need to write code to get the expected result.

If we have one higher level of abstraction present in the microservice environment itself which can perform this task and delivery the result using single end point, it will make it easier for calling system to get whole result in a single API request. This approach is called a gateway pattern in microservice world. API gateway can route the client request to the backend services so it can act as a layer in between client and service. API gateway performs following tasks.


  • Routing the client request to backend service

  • Authenticate incoming request

  • Manage the network traffic

  • filter incoming request

The Spring cloud API Gateway is designed based on Spring webflux and Reactor framework. This is the reason it is non blocking and reacting in nature so it can handle large number of requests.

The routing feature of the gateway is explained in the next example for employee and department services.

Create new spring boot project as discussed in the part-1 and add below dependencies

<dependencies>
		<dependency>
	      <groupId>org.springframework.boot</groupId>
	      <artifactId>spring-boot-starter-actuator</artifactId>
	    </dependency>
	    <dependency>
	      <groupId>io.micrometer</groupId>
	      <artifactId>micrometer-tracing-bridge-brave</artifactId>
	    </dependency>
	    <dependency>
	      <groupId>io.zipkin.reporter2</groupId>
	      <artifactId>zipkin-reporter-brave</artifactId>
	    </dependency>
	    <dependency>
	      <groupId>org.springframework.cloud</groupId>
	      <artifactId>spring-cloud-starter-config</artifactId>
	    </dependency>
	    <dependency>
	      <groupId>org.springframework.cloud</groupId>
	      <artifactId>spring-cloud-starter-gateway</artifactId>
	    </dependency>
	    <dependency>
	      <groupId>org.springframework.cloud</groupId>
	      <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
	    </dependency>
	    <dependency>
	      <groupId>org.springframework.boot</groupId>
	      <artifactId>spring-boot-starter-test</artifactId>
	      <scope>test</scope>
	    </dependency>

	</dependencies>

Add application.yaml file with below configuration to the project

spring:
  application:
   name: gateway-service
   
  config:
   import: "optional:configserver:http://localhost:8088"

Now add the gateway-service.yaml file to the config server with below configuration

spring:
  cloud:
   gateway:
     discovery:
      locator:
        enabled: true 
     routes:
        - id: employee-service
          uri: lb://employee-service
          predicates:
            - Path=/employee/**
        - id: department-service
          uri: lb://department-service
          predicates:
            - Path=/department/**

Here you can note that it is a declarative way to include gateway routes in the API

We can achieve this using programmatic way as well using RouteLocatorBuilder.

Route consist of id, uri, predicates and filters.

Request path : when client sends request to API gateway, the request reach to Gateway Handler Mapping and if the route matches, then request sent to the Gateway web Handler which in turn process the request to filter chain. The proxy request is created once filter processing is complete and then request is finally sent to appropriate end point.


Annotate the GatewayServiceApplication with @EnableDiscoveryClient so that it is registered with eureka server. As you notice, that we are able to map get department request to department service using API gateway.


get department using API gateway
get department using APIGateway

spring boot 3 microservices Unit Testing with Junit 5

Writing JUnit test cases are primary requirements in many production grade applications. Having good number of test cases not only increase the code coverage percentage but also gives confidence from the quality perspective. Having test case coverage for each condition in the code ensures maintainability of the application. In case of modification, the unit test cases help to ensure that each module is intact after the change.

Recent JUnit5 has built in support of three modules including Junit platform, Junit jupeter and Junit vintage.

Let us add few test cases in the employee and department microservices. We need to add following maven dependencies to add junit5 support in employee and department service.

 <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-params</artifactId>
        </dependency>
        <dependency>
            <groupId>org.junit.platform</groupId>
            <artifactId>junit-platform-suite</artifactId>
            <scope>test</scope
        </dependency>

As we are going to write test cases on spring layers including controller, service and repository, we will use @SpringBootTest annotation to run the test cases.


Important thing to note here is we should be able to run the test cases without starting the application so we need a way to load spring container for test case execution support and Spring boot provides support for the same.


Mocking with Mockito

Term Unit Testing refers to testing individual component in isolation. In real time most of the components have dependencies on other service. Therefore we need to include all those dependencies in consideration for testing actual business logic and this becomes complex. In order to solve this issue, we create a dummy or replica of the dependent file or code that is called mocking. As the term suggest, mock object is to support covering the testing of actual piece of code and therefore it should not require testing.


For example, let's take example of findByDepartmentId() method in the EmployeeService class. This method calls the employeeRepository.findByDepartmentId(departmentId) method in turn to fetch the data from the repository layer. If we want to write a test case here our scope is limited to service layer and so we need to mock the employeeRepository class here.

when(employeeRepository.findByDepartmentId(1L)).thenReturn(empList);

We need to use the when().thenReturn construct provided by the mockito. We will put the logic or condition inside the when block which we want to mock and will put the expected result in the thenReturn block. We can construct the expected result before we execute the when().thenReturn statement, this way we are informing mock to return the expected object whenever the test come across when condition. The next step is to execute actual service call which we wan to test which is

List<Employee> fetchedEmpList = employeeService.findByDepartmentId(1L);

Here there is no further processing takes place in service layer except we collect the employee list and return the same but in case if there is any further processing present in the service method, we need to include that as a part of test case. for example once we get the employee list from the repository, we check if that employee is contract employee or permanent. we remove contractual employee from the list and return only permanent employees. Here we need to include additional check to make sure the method only returns permanent employees.


Include below dependency for mockito support.

<dependency>
		    <groupId>org.mockito</groupId>
		    <artifactId>mockito-junit-jupiter</artifactId>
		    <scope>test</scope>
		</dependency>

Code coverage with Jacoco


Jacoco is useful to check how many lines of code is covered by test cases. It is used to measure the coverage of the code for each functionality present in the project. We need to add few properties in the pom.xml file to make it work.

for more details : Jacoco


<configuration-datafile>employee-service/target/jacoco.exec</configuration-datafile>
		<configuration-destfile>employee-service/target/jacoco.exec</configuration-destfile>
		<java.version>17</java.version>
		<spring-cloud.version>2023.0.0</spring-cloud.version>
		<junit.jupiter.version>5.9.1</junit.jupiter.version>
        <junit.platform.version>1.9.1</junit.platform.version>
		<!-- JaCoCo Properties -->
	    <jacoco.version>0.8.8</jacoco.version>
	    <sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
	    <sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
	    <sonar.jacoco.reportPath>${project.basedir}/../target/jacoco.exec</sonar.jacoco.reportPath>
	    <sonar.language>java</sonar.language>

sonar properties are present in case if we want to integrate jacoco with sonarqube server. Also include below dependency to pom.xml file to add jacoco support.


<dependency>
		    <groupId>org.jacoco</groupId> 
		    <artifactId>jacoco-maven-plugin</artifactId>
		    <version>0.8.6</version>
		</dependency>

Now we are ready to run maven command for jacoco as below ;

clean jacoco:prepare-agent install jacoco:report

If you encounter any issues at this point, make sure you have imported the jacoco maven dependency in your project, updated the same and you are running correct Java version, here in this case it is Java 17. Once you execute the above command successfully, you can check your project's target directory and check site, you should see like below



code coverage files eclipse
code coverage files eclipse

The coverage report should look like below


jacoco code coverage report
jacoco code coverage report

This report shows coverage for methods and lines in each class.


Code analysis using sonarqube

Writing a clean code is an essential part of software development. Clean code provides easy to read, maintainable and secure code that will result in boosting performance of the application. Sonar cube is a self manage automatic code review tool that promotes the use of the Clean Code. Sonar cube helps detect issue within your code by running code scan to prevent defect and issues.


There are number of ways in which we can use sonar in our project. If you want to integrate sonar within eclipse, you can go to the market place and download the plugin. Solar tube is also available with the docker image so you can download from Sonar cube official website and run the same. You can download the Sonar cube zip file and perform the local installation as well for the exploration purpose.


In this post, we are considering local installation. Once you download and install the sonarqube, go to installation\conf\sonar.properties and locate sonar.web.port property.

provide respective value here and then start the server.


you can check more about installation and setup here

default user name and password is admin.



sonar qube login
sonar qube login

Once you land onto the home page you can change the default password.

Go To Administration->Security->Action->Enter new password


Now we are set to configure the project into sonar.

Go To Projects menu, click on create project -> local project and add respective values


sonarqube project creation
create project in sonar

Create a separate project for each microservice since each has different repository in git. Later if we want we can integrate with git as well so that sonar can perform the scan on the repository itself.


If we want to setup SonarLint in eclipse, we need token to complete the configuration. Also token is required if we want to run analysis on the project from sonarqube.


sonarqube tokens
sonarqube tokens

you can provide the token timings as per the available options


Now we are set to run the analysis of the project. We can run the Sonar commands either by using the Maven command line or by setting up configuration within eclipse.


eclipse maven configuration to run sonar analysis
eclipse maven configuration to run sonar analysis

Add value for below properties

sonar.token

sonar.projectName

sonar.projectKey


once the run is successful, refresh the sonar page and check the project it should show the quality gate status and also total code coverage.


sonarqube analysis
sonar analysis

Every time you add the code, you can run the analysis to check the above parameters.


Please check the code changes here Git



26 views0 comments

Recent Posts

See All

Comments


bottom of page