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("rgupta.mtech@gmail.com");
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("rgupta.mtech@gmail.com");
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("rgupta.mtech@gmail.com");
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