K. J. Somaiya College of Engineering, Mumbai-77 Title: Testing Pyramid Objective: Understand the Microservices Testing Pyramid and its application to microservices architecture. Expected Outcome of Experiment: CO Outcome CO5 Use latest open source frameworks, programming languages for microservices Books/ Journals/ Websites referred: Related Theory: The testing pyramid is a concept used to categorize and prioritize software tests based on their scope, granularity, and execution speed. The pyramid consists of three layers: unit tests at the base, integration tests in the middle, and end-to-end (E2E) tests at the top. The idea behind the testing pyramid is to encourage a balanced testing strategy that focuses more on smaller, faster tests at the base, while limiting the number of slower, more expensive tests at the top. Department of Computer Engineering FMS Sem-VI – Jan-May 2024 Page - K. J. Somaiya College of Engineering, Mumbai-77 a) Unit Tests (Base of the Pyramid) ● Scope: Focuses on testing individual components or units of code in isolation, such as functions, methods, or classes. ● Granularity: Fine-grained, targeting specific pieces of code. ● Speed: Fast execution, typically running in milliseconds. ● Purpose: Verify that each unit of code performs as expected and meets its design specifications. b) Integration Tests (Middle of the Pyramid) ● Scope: Tests the interactions between different units/modules of the application, ensuring that they work together correctly. ● Granularity: Coarser than unit tests, covering multiple components or services. ● Speed: Slower than unit tests but faster than end-to-end tests, usually running in seconds to minutes. ● Purpose: Validate the integration points between units/modules and catch issues that unit tests might miss. c) End-to-End (E2E) Tests (Top of the Pyramid) ● Scope: Tests the entire application flow from start to finish, simulating real user scenarios. ● Granularity: Broadest scope, covering the complete application or system. ● Speed: Slowest execution, often requiring more setup and teardown, usually running in minutes. ● Purpose: Ensure that the application works as expected from the user's perspective and that all components are integrated correctly. Department of Computer Engineering FMS Sem-VI – Jan-May 2024 Page - K. J. Somaiya College of Engineering, Mumbai-77 Advantages of Testing Pyramid: a) Fast Feedback: Unit tests provide rapid feedback to developers, allowing them to catch and fix issues early in the development cycle. b) Cost-effective: Unit tests are cheaper to write and maintain compared to higher-level tests. c) Focused Coverage: Emphasizes testing the core functionality and logic of the application first before moving to broader, end-to-end scenarios. Implementation Details: 1. Enlist all the Steps followed and various options explored Orderservice i.e. Oservice Department of Computer Engineering FMS Sem-VI – Jan-May 2024 Page - K. J. Somaiya College of Engineering, Mumbai-77 OserviceApplication.java package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class OserviceApplication { public static void main(String[] args) { SpringApplication.run(OserviceApplication.class, args); } } Order.java package com.example.model; import jakarta.persistence.*; import lombok.*; @Entity @Data @Builder @NoArgsConstructor @AllArgsConstructor public class Order { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String status; private String customerName; public Long getId() { // TODO Auto-generated method stub return id; } public Object getStatus() { // TODO Auto-generated method stub return status; } public void setId(Long id) { // TODO Auto-generated method stub this.id = id; } public void setStatus(String status) { // TODO Auto-generated method stub this.status = status; } } Department of Computer Engineering FMS Sem-VI – Jan-May 2024 Page - K. J. Somaiya College of Engineering, Mumbai-77 OrderController.java package com.example.controller; import com.example.model.Order; import com.example.Service.OrderService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/api/v1/orders") public class OrderController { @Autowired private OrderService orderService; @PostMapping public ResponseEntity<Order> createOrder(@RequestBody Order order) { Order savedOrder = orderService.createOrder(order); return ResponseEntity.ok(savedOrder); } @GetMapping("/{id}") public ResponseEntity<Order> getOrderById(@PathVariable Long id) { Order order = orderService.getOrderById(id); return ResponseEntity.ok(order); } @GetMapping public ResponseEntity<List<Order>> getAllOrders() { List<Order> orders = orderService.getAllOrders(); return ResponseEntity.ok(orders); } } OrderRepository package com.example.Repository; import com.example.model.Order; import org.springframework.data.jpa.repository.JpaRepository; public interface OrderRepository extends JpaRepository<Order, Long> { } OrderService package com.example.Service; import com.example.model.Order; import com.example.Repository.OrderRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; Department of Computer Engineering FMS Sem-VI – Jan-May 2024 Page - K. J. Somaiya College of Engineering, Mumbai-77 @Service public class OrderService { @Autowired private OrderRepository orderRepository; public Order createOrder(Order order) { return orderRepository.save(order); } public List<Order> getAllOrders() { return orderRepository.findAll(); } public Order getOrderById(Long id) { return orderRepository.findById(id).orElse(null); } public Order updateOrder(Order updatedOrder) { return orderRepository.save(updatedOrder); } } Tests: OserviceApplicationTests.java package com.example.demo; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class OserviceApplicationTests { @Test void contextLoads() { } } Tests.java package com.example.demo; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.*; import java.util.ArrayList; import java.util.List; import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import com.example.model.Order; import com.example.Repository.OrderRepository; import com.example.Service.OrderService; public class Tests { Department of Computer Engineering FMS Sem-VI – Jan-May 2024 Page - K. J. Somaiya College of Engineering, Mumbai-77 @Mock private OrderRepository orderRepository; @InjectMocks private OrderService orderService; @BeforeEach public void setup() { MockitoAnnotations.openMocks(this); } @Test public void testCreateOrder() { Order order = new Order(); order.setStatus("CREATED"); when(orderRepository.save(order)).thenReturn(order); Order savedOrder = orderService.createOrder(order); assertEquals("CREATED", savedOrder.getStatus()); verify(orderRepository, times(1)).save(order); } @Test public void testGetAllOrders() { List<Order> orders = new ArrayList<>(); orders.add(new Order()); orders.add(new Order()); when(orderRepository.findAll()).thenReturn(orders); List<Order> retrievedOrders = orderService.getAllOrders(); assertEquals(2, retrievedOrders.size()); verify(orderRepository, times(1)).findAll(); } @Test public void testGetOrderById() { Long orderId = 1L; Order order = new Order(); order.setId(orderId); when(orderRepository.findById(orderId)).thenReturn(Optional.of(order)) ; Order retrievedOrder = orderService.getOrderById(orderId); assertEquals(orderId, retrievedOrder.getId()); verify(orderRepository, times(1)).findById(orderId); } @Test public void testUpdateOrder() { Long orderId = 1L; Order updatedOrder = new Order(); updatedOrder.setId(orderId); updatedOrder.setStatus("UPDATED"); when(orderRepository.save(updatedOrder)).thenReturn(updatedOrder); Order savedOrder = orderService.updateOrder(updatedOrder); Department of Computer Engineering FMS Sem-VI – Jan-May 2024 Page - K. J. Somaiya College of Engineering, Mumbai-77 assertEquals("UPDATED", savedOrder.getStatus()); verify(orderRepository, times(1)).save(updatedOrder); } } OUTPUT: Productservice i.e. Pservice Department of Computer Engineering FMS Sem-VI – Jan-May 2024 Page - K. J. Somaiya College of Engineering, Mumbai-77 PserviceApplication.java package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class PserviceApplication { public static void main(String[] args) { SpringApplication.run(PserviceApplication.class, args); } } Product.java package com.example.demo; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; @Entity @Data @AllArgsConstructor @NoArgsConstructor @Builder public class Product { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String description; private double price; private int stock; // Getter and Setter for id public Long getId() { return id; } public void setId(Long id) { this.id = id; } // Getter and Setter for name public String getName() { return name; } public void setName(String name) { this.name = name; Department of Computer Engineering FMS Sem-VI – Jan-May 2024 Page - K. J. Somaiya College of Engineering, Mumbai-77 } // Getter and Setter for description public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } // Getter and Setter for price public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } // Getter and Setter for stock public int getStock() { return stock; } public void setStock(int stock) { this.stock = stock; } } ProductController.java package com.example.demo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/api/v1/products") public class ProductController { @Autowired private ProductService productService; @PostMapping public ResponseEntity<Product> createProduct(@RequestBody Product product) { Product savedProduct = productService.createProduct(product); return ResponseEntity.ok(savedProduct); } @GetMapping("/{id}") public ResponseEntity<Product> getProductById(@PathVariable Long id) { Product product = productService.getProductById(id); return ResponseEntity.ok(product); Department of Computer Engineering FMS Sem-VI – Jan-May 2024 Page - K. J. Somaiya College of Engineering, Mumbai-77 } @GetMapping public ResponseEntity<List<Product>> getAllProducts() { List<Product> products = productService.getAllProducts(); return ResponseEntity.ok(products); } @PutMapping("/{id}") public ResponseEntity<Product> updateProduct(@PathVariable Long id, @RequestBody Product product) { product.setId(id); Product updatedProduct = productService.updateProduct(product); return ResponseEntity.ok(updatedProduct); } @DeleteMapping("/{id}") public ResponseEntity<Void> deleteProduct(@PathVariable Long id) { productService.deleteProduct(id); return ResponseEntity.noContent().build(); } } ProductRepository.java package com.example.demo; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface ProductRepository extends JpaRepository<Product, Long> { } ProductService.java package com.example.demo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Service public class ProductService { @Autowired private ProductRepository productRepository; // Create a new product public Product createProduct(Product product) { return productRepository.save(product); } // Retrieve a product by ID public Product getProductById(Long id) { return productRepository.findById(id).orElse(null); Department of Computer Engineering FMS Sem-VI – Jan-May 2024 Page - K. J. Somaiya College of Engineering, Mumbai-77 } // Retrieve all products public List<Product> getAllProducts() { return productRepository.findAll(); } // Update an existing product public Product updateProduct(Product product) { return productRepository.save(product); } // Delete a product by ID public void deleteProduct(Long id) { productRepository.deleteById(id); } } Tests PserviceApplicationTests.java package com.example.demo; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class PserviceApplicationTests { @Test void contextLoads() { } } ProductServiceTest.java package com.example.demo; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import java.util.ArrayList; import java.util.List; import java.util.Optional; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) public class ProductServiceTest { @Mock private ProductRepository productRepository; Department of Computer Engineering FMS Sem-VI – Jan-May 2024 Page - K. J. Somaiya College of Engineering, Mumbai-77 @InjectMocks private ProductService productService; @Test public void testCreateProduct() { // Create a sample product Product product = new Product(); product.setId(1L); product.setName("Test Product"); product.setDescription("Description of test product"); product.setPrice(9.99); product.setStock(10); // Mock save method of productRepository when(productRepository.save(any(Product.class))).thenReturn(product); // Call the method to create product Product createdProduct = productService.createProduct(product); // Verify that productRepository's save method was called verify(productRepository, times(1)).save(any(Product.class)); // Check if the created product matches the expected product assertEquals(product.getId(), createdProduct.getId()); assertEquals(product.getName(), createdProduct.getName()); assertEquals(product.getDescription(), createdProduct.getDescription()); assertEquals(product.getPrice(), createdProduct.getPrice()); assertEquals(product.getStock(), createdProduct.getStock()); } @Test public void testGetProductById() { // Create a sample product Product product = new Product(); product.setId(1L); product.setName("Test Product"); product.setDescription("Description of test product"); product.setPrice(9.99); product.setStock(10); // Mock findById method of productRepository when(productRepository.findById(1L)).thenReturn(Optional.of(product)); // Call the method to get product by ID Product foundProduct = productService.getProductById(1L); // Verify that productRepository's findById method was called verify(productRepository, times(1)).findById(1L); // Check if the found product matches the expected product assertEquals(product.getId(), foundProduct.getId()); assertEquals(product.getName(), foundProduct.getName()); assertEquals(product.getDescription(), foundProduct.getDescription()); assertEquals(product.getPrice(), foundProduct.getPrice()); Department of Computer Engineering FMS Sem-VI – Jan-May 2024 Page - K. J. Somaiya College of Engineering, Mumbai-77 assertEquals(product.getStock(), foundProduct.getStock()); } @Test public void testGetAllProducts() { // Create a list of sample products List<Product> productList = new ArrayList<>(); productList.add(new Product()); productList.add(new Product()); // Mock findAll method of productRepository when(productRepository.findAll()).thenReturn(productList); // Call the method to get all products List<Product> allProducts = productService.getAllProducts(); // Verify that productRepository's findAll method was called verify(productRepository, times(1)).findAll(); // Check if the returned list matches the expected list assertEquals(productList.size(), allProducts.size()); assertEquals(productList.get(0).getId(), allProducts.get(0).getId()); assertEquals(productList.get(0).getName(), allProducts.get(0).getName()); assertEquals(productList.get(0).getDescription(), allProducts.get(0).getDescription()); assertEquals(productList.get(0).getPrice(), allProducts.get(0).getPrice()); assertEquals(productList.get(0).getStock(), allProducts.get(0).getStock()); assertEquals(productList.get(1).getId(), allProducts.get(1).getId()); assertEquals(productList.get(1).getName(), allProducts.get(1).getName()); assertEquals(productList.get(1).getDescription(), allProducts.get(1).getDescription()); assertEquals(productList.get(1).getPrice(), allProducts.get(1).getPrice()); assertEquals(productList.get(1).getStock(), allProducts.get(1).getStock()); } @Test public void testUpdateProduct() { // Create a sample product Product product = new Product(); product.setId(1L); product.setName("Updated Product"); product.setDescription("Updated Description"); product.setPrice(19.99); product.setStock(20); // Mock save method of productRepository Department of Computer Engineering FMS Sem-VI – Jan-May 2024 Page - K. J. Somaiya College of Engineering, Mumbai-77 when(productRepository.save(any(Product.class))).thenReturn(product); // Call the method to update product Product updatedProduct = productService.updateProduct(product); // Verify that productRepository's save method was called verify(productRepository, times(1)).save(any(Product.class)); // Check if the updated product matches the expected product assertEquals(product.getId(), updatedProduct.getId()); assertEquals(product.getName(), updatedProduct.getName()); assertEquals(product.getDescription(), updatedProduct.getDescription()); assertEquals(product.getPrice(), updatedProduct.getPrice()); assertEquals(product.getStock(), updatedProduct.getStock()); } @Test public void testDeleteProduct() { // Call the method to delete product productService.deleteProduct(1L); // Verify that productRepository's deleteById method was called verify(productRepository, times(1)).deleteById(1L); } } OUTPUT: Conclusion:- Successfully learnt and understood the Testing Pyramid in microservices (implemented through SpringBoot) Department of Computer Engineering FMS Sem-VI – Jan-May 2024 Page -