0% found this document useful (0 votes)
7 views

spring boot fundamentals

The document outlines a workshop on Spring and Spring Boot, covering key topics such as Dependency Injection, REST basics, Spring Boot application setup, and microservice architecture. It includes detailed sessions on building a RESTful CRUD application, configuring properties for different databases, and implementing logging and exception handling. Additionally, it discusses Spring Boot features like auto-configuration, actuator, and documentation using OpenAPI 3.0.

Uploaded by

Suresh
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views

spring boot fundamentals

The document outlines a workshop on Spring and Spring Boot, covering key topics such as Dependency Injection, REST basics, Spring Boot application setup, and microservice architecture. It includes detailed sessions on building a RESTful CRUD application, configuring properties for different databases, and implementing logging and exception handling. Additionally, it discusses Spring Boot features like auto-configuration, actuator, and documentation using OpenAPI 3.0.

Uploaded by

Suresh
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 24

Spring with Spring boot workshop

___________________________

Key topics:
==================
=> Understand Depdendency Injection
=> bean wiring :annoation, java configuraton
=> REST basics
=> Spring boot REST application
=> Spring data
=> Spring boot web application, Spring MVC arch
=> spring boot profile
=> spring boot actuator
=>Spring boot Monotring and Logging
=> Spring boot testing
=> Spring boot security
=> Spring Boot microservice architecture

Session 1:
Understand Depdendency Injection

What is the need of spring framework?


-----------------------------------

Lets take a example:

Rest
Controller <------------ Service layer <---------- persistance layer <------ db

ravi team sumit team kapil team

what sping does it produce loose coupling between the layer


Take example lets say ravi team creating controller layer, sumit team is creating
service layer and kapil team is
creating persitance layer... now controller layer need service and service layer
need persistance layer

as we have design our application as per interface and we have use DI therefore
kapil team can change implemenation
of service layer ( let earlier they are using Jdbc now wnat to use hibernate )
without effectiving even a single line
of code in Service layer (sumit team) do you not think it is great...

beside that spring DI help to manage dependency of our project and make our project
flexiable

---------- ProductDaoImplHib
|
ProductService <---------------- ProductDao-------ProductDaoImplJdbc
|
---------- ProductDaoImplUtil

public class Product {


private Integer id;
private String name;
private int price;
}

public interface ProductDao {


public List<Product>getProducts();
}

public interface ProductDaoImplUtil implements ProductDao {


public List<Product>getProducts(){
//collection implementation ....
}
}

public interface ProductDaoImplJdbc implements ProductDao {


public List<Product>getProducts(){
//jdbc implementation ....
}
}

public interface ProductService {


public List<Product>getProducts();
}

public interface ProductServiceImpl implements ProductService{

private ProductDao productDao;

public ProductServiceImpl(){
productDao=new ProductDaoImplUtil(); // or ProductDaoImplJdbc()
}

public List<Product>getProducts(){
// business logic
}
}

=> we have to change the implementation ...whenever we swap dao layer :(

Spring BeanFactory vs ApplicationContext


______________________________________

* BeanFactory:
- light weight container , dont support many featues
- dont use it
BeanFactory applicationContext=new XmlBeanFactory(new
ClassPathResource("demo.xml"));

* ApplicationContext
- more powerful
ApplicationContext applicationContext=
new ClassPathXmlApplicationContext("demo.xml");

ApplicationContext
-ClassPathXmlApplicationContext
-FileSystemXmlApplicationContext
-AnnotationConfigApplicationContext
-XMLWebApplicationContext

spring bean configuration:


________________________
1. xml configuration
2. annotation configuration
3. java configuration

session 2:
___________
Spring vs Spring boot

Advantage of spring boot , configuration spring boot


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Advantage of spring boot
_____________________

=> Auto-Configuration

=> Dependency Management

=> Externalized Configuration


bean can be configured through application.properties file
without touching java or xml config

=> Production support


We get health checking, application and jvm metrics,
jmx via http and a few more things for free

=> Runnable Jars


We can package your application as a runnable jar with embedded tomcat
included so it presents a self-contained deployment unit

=> Microservice

configuration spring boot


____________________

create spring boot project: choose web, jpa, derby....

//Configuration, @EnableAutoConfiguration and @ComponentScan

@SpringBootApplication
public class Application {

public static void main(String[] args) {


ApplicationContext ctx = SpringApplication.run(Application.class, args);

System.out.println("Let's inspect the beans provided by Spring Boot:");

String[] beanNames = ctx.getBeanDefinitionNames();


Arrays.sort(beanNames);
for (String beanName : beanNames) {
System.out.println(beanName);
}
}

spring-boot:run

@RestController
public class HelloRestController {

@RequestMapping("/hello")
public String hello(){
return "spring boot";
}
}

public class Product {


private Integer id;
private String name;
private int price;
}

@GetMapping("products/{id}")
public Book getProductById(@PathVariable(name = "id")int id) {
return new Product(id, "java basics book", 300);
}

application.properties
---------------------------
server.servlet.context-path=/productapp
server.port=8080

bannner:
________________
spring.banner.location=classpath:banner.txt

https://devops.datenkollektiv.de/banner.txt/index.html
How to run spring boot app with command line:
java -jar jpa_demo2-0.0.1-SNAPSHOT.jar --server.port=8050

session 3: spring boot rest crud application


___________________________________________

Rest
Controller <------------ Service layer <---------- persistance layer <------
SessionFactory

step 1: application.properties
_______________________

in case of h2 database :
---------------------
server.port=8090
server.servlet.context-path=/productapp
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
# Custom H2 Console URL
spring.h2.console.path=/h2

spring.jpa.hibernate.ddl-auto=update

logging.level.org.springframework.web: DEBUG
logging.level.org.hibernate: ERROR

spring.jpa.show-sql=true

in case of mysql database :


---------------------
server.servlet.context-path=/productapp
server.port=8082

spring.datasource.url=jdbc:mysql://localhost:3306/demoms?useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.jpa.hibernate.ddl-auto=update
logging.level.org.springframework.web: DEBUG
logging.level.org.hibernate: ERROR
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

UserInterface.PRODUCT_ADD_SUCCESS=product added successfully


UserInterface.PRODUCT_UPDATE_SUCCESS=product added successfully
UserInterface.PRODUCT_DELETE_SUCCESS=product added successfully

Service.PRODUCT_NOT_EXISTS=Product not exist

Step 2: dao layer


_______________________
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Entity
@Table(name = "product_table")
public class Product {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private int price;
public Product(String name, Double price) {
super();
this.name = name;
this.price = price;
}

@Repository
public interface ProductDao extends JpaRepository<Product, Integer>{
public Product findByName(String name);
}

Step 3: service layer


_______________________

public interface ProductService {


public List<Product> findAll();
public Product getById(int id);
public Product addProduct(Product product);
public Product updateProduct(int id, Product product);
public Product deleteProduct(int id);

public class ProductNotFoundException extends RuntimeException{


public ProductNotFoundException(String message) {
super(message);
}
}
@Service
@Transactional
public class ProductServiceImpl implements ProductService {

private ProductDao productDao;

@Autowired
public ProductServiceImpl(ProductDao productDao) {
this.productDao = productDao;
}

@Override
public List<Product> findAll() {
return productDao.findAll();
}

@Override
public Product getById(int id) {
return productDao.findById(id)
.orElseThrow(() -> new ProductNotFoundException("product
with id" + id + " is not found"));
}

@Override
public Product addProduct(Product product) {
productDao.save(product);
return product;
}

@Override
public Product updateProduct(int id, Product product) {
Product productToUpdate= getById(id);
productToUpdate.setPrice(product.getPrice());
productDao.save(productToUpdate);
return productToUpdate;
}

@Override
public Product deleteProduct(int id) {
Product productToDelete= getById(id);
productDao.delete(productToDelete);
return productToDelete;
}

Step 4: rest controller


_______________________
@RestController
public class ProductController {

private ProductService productService;

@Autowired
public ProductController(ProductService productService) {
this.productService = productService;
}
@GetMapping(path = "products")
public List<Product>findAll(){
return productService.findAll();
}

@GetMapping(path = "products/{id}")
public Product findById(@PathVariable(name = "id") int id){
return productService.getById(id);
}

@PostMapping(path = "products")
public Product addProduct( @RequestBody Product product){
return productService.addProduct(product);
}

@DeleteMapping(path = "products/{id}")
public Product deleteProduct(@PathVariable(name = "id") int id){
return productService.deleteProduct(id);
}

@PutMapping(path = "products/{id}")
public Product updateProduct(@PathVariable(name = "id") int id, @RequestBody
Product product){
return productService.updateProduct(id, product);
}
}

@ResponseStatus(HttpStatus.CREATED)

Step 5: rest controller: ResponseEntity


_______________________________________

@RestController
public class ProductController {

private ProductService productService;

@Autowired
public ProductController(ProductService productService) {
this.productService = productService;
}

@GetMapping(path = "products")
public ResponseEntity<List<Product>> findAll(){
return
ResponseEntity.status(HttpStatus.OK).body(productService.findAll());
}

@GetMapping(path = "products/{id}")
public ResponseEntity<Product> findById(@PathVariable(name = "id") int id){
return ResponseEntity.ok(productService.getById(id));
}

@PostMapping(path = "products")
public ResponseEntity<Product> addProduct( @RequestBody Product product){
return
ResponseEntity.status(HttpStatus.CREATED).body(productService.addProduct(product));
}
@DeleteMapping(path = "products/{id}")
public ResponseEntity<Void> deleteProduct(@PathVariable(name = "id") int id){
productService.deleteProduct(id);
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}

@PutMapping(path = "products/{id}")
public ResponseEntity<Product> updateProduct(@PathVariable(name = "id") int
id, @RequestBody Product product){
return
ResponseEntity.status(HttpStatus.CREATED).body(productService.updateProduct(id,
product));
}
}

Step 6: rest controller exception handling


_______________________________________
@ResponseStatus(code =HttpStatus.NOT_FOUND)
ProductNotFoundException extends RuntimeExcetion{
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ErrorDetails {
private String message;
private String details;

private String name;


private Date date;

@RestControllerAdvice
public class ExHandler {

@ExceptionHandler(ProductNotFoundException.class)
public ResponseEntity<ErrorDetails> handle404(Exception ex, WebRequest req){
ErrorDetails details=new ErrorDetails();
details.setDate(new Date());
details.setDetails(req.getDescription(true));
details.setName("[email protected]");
details.setDetails(ex.toString());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(details);
}

@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorDetails> handle500(Exception ex, WebRequest req){
ErrorDetails details=new ErrorDetails();
details.setDate(new Date());
details.setDetails(req.getDescription(true));
details.setName("[email protected]");
details.setDetails(ex.toString());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(details);
}

@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorDetails> handle500(Exception ex, WebRequest req){
ErrorDetails details=new ErrorDetails();
details.setDate(new Date());
details.setDetails(req.getDescription(true));
details.setName("[email protected]");
details.setDetails(ex.toString());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(details);
}

Step 7: Supporting both xml and json:


_______________________________

Step 1: put parser for xml

<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>

Step 2:
@GetMapping(path = "products", produces = {MediaType.APPLICATION_JSON_VALUE,
MediaType.APPLICATION_XML_VALUE})

Step 7:spring boot profile


----------------------------

Step 8: spring boot reading property files:


---------------------------------

1. @Value annotation
2. Enviornment
3. @ConfigrationProperties

@EnableConfigurationProperties(Config.class)

@ConfigrationProperties(prefix="db")

Step 9: spring boot logging:


-------------------

Disable logging :
---------------
logging.level.root=OFF
logging.level.org.springframework.boot=OFF
spring.main.banner-mode=OFF

Customizing logging :
---------------
logging.level.org.springframework.web: DEBUG
logging.level.org.hibernate: ERROR

logging.level.com.productapp=info

logging.level.org.springframework.web: DEBUG
logging.level.org.hibernate: ERROR

# Logging pattern for the console


logging.pattern.console= "%d{yyyy-MM-dd } [%thread] %-5level %logger{36} - %msg%n"
#logging pattern for file
logging.pattern.file= "%d{yyyy-MM-dd } [%thread] %-5level %logger{36} - %msg%n"

logging.file.name=/home/raj/Desktop/logs/server.log

step 10. Spring boot documentation Using OpenAPI 3.0


_____________________
https://www.baeldung.com/spring-rest-openapi-documentation

=> Documenting a Spring REST API Using OpenAPI 3.0


=>Swagger is almost equivalent to SOAP formate, used for documentation of REST api

Step 1:

<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.2.0</version>
</dependency>

Step 2:

http://localhost:8090/bookapp/v3/api-docs

http://localhost:8090/bookapp/swagger-ui/index.html

http://localhost:8090/bookapp/v3/api-docs.yaml

Step 3:
Customization location
springdoc.swagger-ui.path=/swagger-ui-bookapp.html

@OpenAPIDefinition(info = @Info(title = "bookapp API", version = "2.0" ,


description = "YMSLI bookapp API"))
public class BookappApplication implements CommandLineRunner {
}
@OpenAPIDefinition(
info = @Info(
title = "",
description = "",
version = "v1",
contact = @Contact(
name = "",
email = "",
url = ""
),
license = @License(
name = "Apache 2.0",
url = "https://www.cbre.com"
)
),
externalDocs = @ExternalDocumentation(
description = "",
url = "https://www.cbre.com/swagger-ui.html"
)
)

Step 11: Spring boot Actuator:


-------------------------

server.port=8080
spring.devtools.restart.enabled=true
#management.endpoints.web.exposure.exclude=*
management.endpoints.web.exposure.include=health, custom-endpoint
management.endpoint.health.show-details=always
management.health.disk.enabled=true
management.health.livenessstate.enabled=true
management.health.readinessstate.enabled=true
management.server.port=9090

#/actuator/info endpoint will show the information given here, keys


# that are started with info
info.app.encoding=UTF-8
info.app.java.source=11
info.app.java.target=11
info.app.name=spring booot actuator

Custom end points

@Configuration
@Endpoint(id = "custom-endpoint")
public class CustomEndpoints {
@ReadOperation
public String getCustomData(){
return "This is custom Data";
}
}
Step 11: deployment spring boot as war file to tomcat
-------------------------------------------------------

-----------imp property file configurations------------------

server.port=8090
server.servlet.context-path=/bankapp
spring.datasource.driver-class-name= com.mysql.jdbc.Driver
spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto= update
spring.datasource.url=jdbc:mysql://localhost:3306/edu123?useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

spring.jpa.hibernate.ddl-auto=update

# if u want to disable logging


#logging.level.root=OFF
#logging.level.org.springframework.boot=OFF
#spring.main.banner-mode=OFF

logging.level.org.springframework.web: DEBUG
logging.level.org.hibernate: ERROR
logging.level.com.productapp: INFO
logging.level.com.productapp.service: INFO

logging.pattern.console= "%d{yyyy-MM-dd } [%thread] %-5level %logger{36} - %msg%n"


#logging pattern for file
logging.pattern.file= "%d{yyyy-MM-dd } [%thread] %-5level %logger{36} - %msg%n"
#i wnat to send logs to a specific file?

spring.jpa.show-sql=true
spring.banner.location=

spring.jmx.enabled=true
management.endpoints.web.exposure.include=*
management.endpoints.jmx.exposure.include=*

management.info.env.enabled=true
info.app.encoding=UTF-8
info.app.java.source=21
info.app.java.target=21
info.app.name=productapp
info.app.dev=amit ku

management.endpoint.health.show-details=always
management.endpoint.health.probes.enabled=true
# livenessstate readinessstate
#management.health.livenessstate.enabled=true
#management.health.readinessstate.enabled=true

UserInterface.INTERNAL_SERVER_ERROR=some internal server error


#UserInterface.PRODUCT_ADD_SUCCESS=product added successfully
#UserInterface.PRODUCT_UPDATE_SUCCESS=product added successfully
#UserInterface.PRODUCT_DELETE_SUCCESS=product added successfully
#
#Service.PRODUCT_NOT_EXISTS=Product not exist
#
#
#product.name.absent=Please provide product name
#product.name.invalid=product Name should contain only alphabets and space
#
#product.price.absent=Please provide product price
#account.price.invalid=Please provide correct price bw 100 to 100000

info.key=default
spring.profiles.active=test

server.port=8090
server.servlet.context-path=/productapp
spring.h2.console.enabled=true

spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=foo
spring.datasource.password=foo
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

# Custom H2 Console URL


spring.h2.console.path=/h2

spring.jpa.hibernate.ddl-auto=update

Spring boot testing


---------------------
* Junit mockito intro
* Spring boot testing
* Communication bw spring boot applications

====================
repo layer testing
====================

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.annotation.Rollback;

import java.util.List;
import java.util.Optional;

import static org.assertj.core.api.Assertions.assertThat;


@DataJpaTest
class ProductRepoTest {

@Autowired
private ProductRepo productRepo;
private Product product;

@BeforeEach
void setUp() {
product=new Product("laptop",120000);
}

@DisplayName("given Product Object When Saved Then Retun Product Object")


@Test
@Rollback(value = true)
public void givenProductObjectWhenSavedThenRetunProductObject(){
Product savedProduct= productRepo.save(product);
assertThat(savedProduct).isNotNull();
assertThat(savedProduct.getId()).isGreaterThan(0);
}

@DisplayName("givenProductListWhenFindAllThenRetunProductList")
@Test
public void givenProductListWhenFindAllThenRetunProductList(){
//given
Product product2=new Product("laptop cover",1200);
productRepo.save(product);
productRepo.save(product2);
//when
List<Product> productList=productRepo.findAll();
//then verify
assertThat(productList).isNotNull();
assertThat(productList.size()).isEqualTo(2);
}
@DisplayName("JUnit test for get product by id operation")
@Test
public void givenProductObject_whenFindById_thenReturnProductObject(){
// given - precondition or setup
Product p1=new Product("laptop",120000);
productRepo.save(p1);

// when - action or the behaviour that we are going test


Product productDB = productRepo.findById(p1.getId()).get();
// then - verify the output
assertThat(productDB).isNotNull();
}
@DisplayName("JUnit test for update product operation")
@Test
public void givenEmployeeObject_whenUpdateProduct_thenReturnUpdatedProduct(){
// given - precondition or setup
Product p1=new Product("laptop",120000);
productRepo.save(p1);
// when - action or the behaviour that we are going test
Product savedProduct = productRepo.findById(p1.getId()).get();
savedProduct.setPrice(130000);
Product updatedProduct = productRepo.save(savedProduct);
assertThat(updatedProduct.getPrice()).isEqualTo(130000);
}

@DisplayName("JUnit test for delete product operation")


@Test
public void givenProductObject_whenDelete_thenRemoveProduct(){
// given - precondition or setup
Product p1=new Product("laptop",120000);
productRepo.save(p1);

// when - action or the behaviour that we are going test


productRepo.deleteById(p1.getId());
Optional<Product> employeeOptional = productRepo.findById(product.getId());

// then - verify the output


assertThat(employeeOptional).isEmpty();
}

@AfterEach
void tearDown() {
}
}

====================
service layer testing
====================

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.BDDMockito;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.Collections;
import java.util.List;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;


import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.BDDMockito.given;

//i want to mock dao layer


@ExtendWith(MockitoExtension.class)
class ProductServiceImplTest {

@Mock
private ProductRepo productRepo;

@InjectMocks
private ProductServiceImpl productService;

private Product product;

@BeforeEach
void setUp() {
product=new Product(1,"laptop",120000);
}

@DisplayName("JUnit test for save Product method")


@Test
public void givenProductObject_whenSaveProduct_thenReturnProductObject(){
// given - precondition or setup
given(productRepo.save(product)).willReturn(product);
Product savedProduct = productService.addProduct(product);
assertThat(savedProduct).isNotNull();
}

@DisplayName("JUnit test for getAll Product method")


@Test
public void givenProductList_whenGetAllProduct_thenReturnProductList(){
// given - precondition or setup
Product product2=new Product("laptop cover", 1200);

given(productRepo.findAll()).willReturn(List.of(product,product2));

// when - action or the behaviour that we are going test


List<Product> productList = productService.getAll();

// then - verify the output


Assertions.assertThat(productList).isNotNull();
Assertions.assertThat(productList.size()).isEqualTo(2);
}

@DisplayName("JUnit test for getAll Product method (negative scenario)")


@Test
public void
givenEmptyEmployeesList_whenGetAllEmployees_thenReturnEmptyEmployeesList(){
// given - precondition or setup
given(productRepo.findAll()).willReturn(Collections.emptyList());

// when - action or the behaviour that we are going test


List<Product> employeeList = productService.getAll();
// then - verify the output
Assertions.assertThat(employeeList).isEmpty();
Assertions.assertThat(employeeList.size()).isEqualTo(0);
}

@AfterEach
void tearDown() {
}

==========================
controller layer testing
===========================

import com.fasterxml.jackson.databind.ObjectMapper;
import com.productapp.repo.Product;
import com.productapp.service.ProductService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;

import java.util.ArrayList;
import java.util.List;

import static org.hamcrest.CoreMatchers.is;


import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.willDoNothing;
import static
org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static
org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static
org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static
org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest(controllers = ProductRestController.class)
class ProductRestControllerTest {

@Autowired
private MockMvc mockMvc;

@MockBean
private ProductService productService;

@Autowired
private ObjectMapper objectMapper;

@Test
public void givenProductObject_whenCreateProduct_thenReturnSavedProduct()
throws Exception{

// given - precondition or setup


Product product=new Product("laptop", 120000);
given(productService.addProduct(any(Product.class)))
.willAnswer((invocation)-> invocation.getArgument(0));

// when - action or behaviour that we are going test


ResultActions response = mockMvc.perform(post("/products")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(product)));

// then - verify the result or output using assert statements


response.andDo(print()).
andExpect(status().isCreated())
.andExpect(jsonPath("$.name",
is(product.getName())))
.andExpect(jsonPath("$.price",
is(product.getPrice())));
}
@Test
public void givenListOfProducts_whenGetAllProducts_thenReturnProductList()
throws Exception{
// given - precondition or setup
List<Product> listOfProducts = new ArrayList<>();
listOfProducts.add(Product.builder().name("a").price(6000).build());
listOfProducts.add(Product.builder().name("b").price(6000).build());
given(productService.getAll()).willReturn(listOfProducts);

// when - action or the behaviour that we are going test


ResultActions response = mockMvc.perform(get("/products"));

// then - verify the output


response.andExpect(status().isOk())
.andDo(print())
.andExpect(jsonPath("$.size()",
is(listOfProducts.size())));

// positive scenario - valid product id


@Test
public void givenProductId_whenGetProductById_thenReturnProductObject() throws
Exception{
// given - precondition or setup
int productId = 1;
Product product=new Product(1,"laptop", 120000);
given(productService.getById(productId)).willReturn(product);

// when - action or the behaviour that we are going test


ResultActions response = mockMvc.perform(get("/products/{id}", productId));

// then - verify the output


response.andExpect(status().isOk())
.andDo(print())
.andExpect(jsonPath("$.name", is(product.getName())))
.andExpect(jsonPath("$.price", is(product.getPrice())));

// @Test
public void givenInvalidProductId_whenGetProductById_thenReturnEmpty() throws
Exception{
// given - precondition or setup
int productId = 1;
Product product=new Product(1,"laptop", 120000);
given(productService.getById(productId)).willReturn(null);

// when - action or the behaviour that we are going test


ResultActions response = mockMvc.perform(get("/products/{id}", productId));

// then - verify the output


response.andExpect(status().isNotFound())
.andDo(print());

// JUnit test for delete employee REST API


@Test
public void givenProductId_whenDeleteProduct_thenReturn200() throws Exception{
// given - precondition or setup
int productId = 1;
willDoNothing().given(productService).deleteProduct(productId);

// when - action or the behaviour that we are going test


ResultActions response = mockMvc.perform(delete("/products/{id}",
productId));

// then - verify the output


response.andExpect(status().isOk())
.andDo(print());
}
}

==========================
integration layer testing
===========================

import com.fasterxml.jackson.databind.ObjectMapper;
import com.productapp.repo.Product;
import com.productapp.repo.ProductRepo;
import com.productapp.service.ProductService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;

import java.util.ArrayList;
import java.util.List;

import static org.hamcrest.CoreMatchers.is;


import static
org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static
org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static
org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static
org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
public class ProductAppIntegrationTest {
@Autowired
private MockMvc mockMvc;

@Autowired
private ProductService productService;

@Autowired
private ProductRepo productRepo;

@Autowired
private ObjectMapper objectMapper;

@BeforeEach
void setup(){
productRepo.deleteAll();
}

@Test
public void givenProductObject_whenCreateProduct_thenReturnSavedProduct()
throws Exception{

// given - precondition or setup


Product product = Product.builder()
.name("watch")
.price(7000)
.build();

// when - action or behaviour that we are going test


ResultActions response = mockMvc.perform(post("/products")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(product)));

// then - verify the result or output using assert statements


response.andDo(print()).
andExpect(status().isCreated())
.andExpect(jsonPath("$.name",
is(product.getName())))
.andExpect(jsonPath("$.price",
is(product.getPrice())));

@Test
public void givenListOfProducts_whenGetAllProducts_thenReturnProductList()
throws Exception{
// given - precondition or setup
List<Product> listOfProducts = new ArrayList<>();
listOfProducts.add(Product.builder().name("foo").price(7000).build());
listOfProducts.add(Product.builder().name("bar").price(7000).build());
productRepo.saveAll(listOfProducts);
// when - action or the behaviour that we are going test
ResultActions response = mockMvc.perform(get("/products"));

// then - verify the output


response.andExpect(status().isOk())
.andDo(print())
.andExpect(jsonPath("$.size()",
is(listOfProducts.size())));

// positive scenario - valid employee id


// JUnit test for GET employee by id REST API
@Test
public void givenProductId_whenGetProductById_thenReturnProducteObject() throws
Exception{
// given - precondition or setup
Product product = Product.builder()
.name("watch")
.price(7000)
.build();
productRepo.save(product);

// when - action or the behaviour that we are going test


ResultActions response = mockMvc.perform(get("/products/{id}",
product.getId()));

// then - verify the output


response.andExpect(status().isOk())
.andDo(print())
.andExpect(jsonPath("$.name", is(product.getName())))
.andExpect(jsonPath("$.price", is(product.getPrice())));

// JUnit test for delete employee REST API


@Test
public void givenProductId_whenDeleteProduct_thenReturn200() throws Exception{
// given - precondition or setup
Product product = Product.builder()
.name("watch")
.price(7000)
.build();
productRepo.save(product);
// when - action or the behaviour that we are going test
ResultActions response = mockMvc.perform(delete("/products/{id}",
product.getId()));

// then - verify the output


response.andExpect(status().isOk())
.andDo(print());
}
}

jacoco
========
https://medium.com/@truongbui95/jacoco-code-coverage-with-spring-boot-835af8debc68

jacoco plugin:
----------------

<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco.version}</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<configuration>
<excludes>
<!-- com.productapp.Productapp01Application

com.productapp.exceptions.ProductNotFoundException
com.productapp.dto.ErrorInfo
com.productapp.repo.Product
-->

<exclude>com/productapp/Productapp01Application.class</exclude>

<exclude>com/productapp/exceptions/ProductNotFoundException.class</exclude>

<exclude>com/productapp/dto/ErrorInfo.class</exclude>

<exclude>com/productapp/repo/Product.class</exclude>
</excludes>
</configuration>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
<execution>
<id>jacoco-check</id>
<goals>
<goal>check</goal>
</goals>
<configuration>
<rules>
<rule>
<element>PACKAGE</element>
<limits>
<limit>
<counter>LINE</counter>

<value>COVEREDRATIO</value>

<minimum>00%</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

sonarcube with spring boot:


===========================
https://medium.com/@truongbui95/jacoco-code-coverage-with-spring-boot-835af8debc68

https://blog.stackademic.com/integratation-of-sonarqube-with-springboot-
6d2cebd4ef95

sonarcube 9.9.4 lts


https://www.sonarsource.com/products/sonarqube/downloads/

https://blog.stackademic.com/integratation-of-sonarqube-with-springboot-
6d2cebd4ef95

mvn clean verify sonar:sonar -Dsonar.projectKey=project1


-Dsonar.host.url=http://localhost:9000 -
Dsonar.login=sqp_c728475131a378fa2c93963c688a9450b3563b6d

You might also like