From a151c7c239ff23db805850a977513f6d8fab828b Mon Sep 17 00:00:00 2001 From: yevr19 Date: Sat, 16 Sep 2023 22:58:38 +0200 Subject: [PATCH] test(product): add web layer tests for product package --- .../endpoint/ProductsEndpointTest.java | 224 ++++++++---------- ...{ProductUtilStub.java => ProductStub.java} | 24 +- 2 files changed, 117 insertions(+), 131 deletions(-) rename src/test/java/com/zufar/onlinestore/product/util/{ProductUtilStub.java => ProductStub.java} (77%) diff --git a/src/test/java/com/zufar/onlinestore/product/endpoint/ProductsEndpointTest.java b/src/test/java/com/zufar/onlinestore/product/endpoint/ProductsEndpointTest.java index def644dd..a10ddfa3 100644 --- a/src/test/java/com/zufar/onlinestore/product/endpoint/ProductsEndpointTest.java +++ b/src/test/java/com/zufar/onlinestore/product/endpoint/ProductsEndpointTest.java @@ -9,7 +9,6 @@ import com.zufar.onlinestore.security.jwt.filter.JwtAuthenticationProvider; import org.instancio.Instancio; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; @@ -25,7 +24,7 @@ import java.util.UUID; import static com.zufar.onlinestore.product.endpoint.ProductsEndpoint.PRODUCTS_URL; -import static com.zufar.onlinestore.product.util.ProductUtilStub.buildSampleProducts; +import static com.zufar.onlinestore.product.util.ProductStub.buildSampleProducts; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -53,6 +52,12 @@ class ProductsEndpointTest { private ProductListWithPaginationInfoDto productList; + @BeforeEach + void setUp() { + productInfo = Instancio.create(ProductInfoDto.class); + productList = buildSampleProducts(0, 10, "name", Sort.Direction.ASC); + } + @Test void whenGetProductsSortedByNameDescThenReturnProducts() throws Exception { int page = 0; @@ -77,125 +82,100 @@ void whenGetProductsSortedByNameDescThenReturnProducts() throws Exception { verify(productApi).getProducts(page, size, sortAttribute, sortDirection.name()); } - @Nested - class ProductsEndpointTests { - @BeforeEach - void setUp() { - productInfo = Instancio.create(ProductInfoDto.class); - productList = buildSampleProducts(0, 10, "name", Sort.Direction.ASC); - } - - @Test - void whenGetProductByIdThenReturn200() throws Exception { - UUID productId = UUID.randomUUID(); - - when(productApi.getProduct(productId)).thenReturn(productInfo); - - mockMvc.perform(get(PRODUCTS_URL + "/{productId}", productId) - .contentType(MediaType.APPLICATION_JSON_VALUE)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.id").value(productInfo.id().toString())); - - verify(productApi).getProduct(productId); - } - - @Test - void whenGetProductsThenReturn200() throws Exception { - int page = 1; - int size = 10; - String sortAttribute = "name"; - String defaultDirection = Sort.Direction.ASC.name(); - - when(productApi.getProducts(page, size, sortAttribute, defaultDirection)).thenReturn(productList); - - mockMvc.perform(get(PRODUCTS_URL) - .param("page", String.valueOf(page)) - .param("size", String.valueOf(size)) - .param("sort_attribute", sortAttribute) - .param("sort_direction", defaultDirection) - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) - .andExpect(jsonPath("$.products.size()").value(productList.products().size())); - - verify(productApi).getProducts(page, size, sortAttribute, defaultDirection); - } - - @Test - void whenNullProductIdThenReturn404() throws Exception { - UUID productId = UUID.randomUUID(); - String errorDescription = buildErrorDescription( - ProductsEndpoint.class.getName() - ); - - when(productApi.getProduct(productId)).thenThrow(new ProductNotFoundException(productId)); - - MvcResult mvcResult = mockMvc.perform(get(PRODUCTS_URL + "/{productId}", productId)) - .andExpect(status().isNotFound()) - .andReturn(); - - String actualResponse = mvcResult.getResponse().getContentAsString(); - ApiResponse expectedResponse = createExpectedErrorResponse(errorDescription, productId); - String expectedResponseBody = objectMapper.writeValueAsString(expectedResponse); - - assertThat(actualResponse).isEqualTo(expectedResponseBody); - - verify(productApi).getProduct(productId); - } - - @Test - void whenGetProductsSortedByNameAscThenReturnProducts() throws Exception { - int page = 0; - int size = 10; - String sortAttribute = "name"; - Sort.Direction sortDirection = Sort.Direction.ASC; - - when(productApi.getProducts(page, size, sortAttribute, sortDirection.name())).thenReturn(productList); - - mockMvc.perform(get(PRODUCTS_URL) - .param("page", String.valueOf(page)) - .param("size", String.valueOf(size)) - .param("sort_attribute", sortAttribute) - .param("sort_direction", sortDirection.name()) - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) - .andExpect(jsonPath("$.products[0].name").value("Product A")) - .andExpect(jsonPath("$.totalElements").value(productList.totalElements())); - - verify(productApi).getProducts(page, size, sortAttribute, sortDirection.name()); - } - - @Test - void whenFirstPageRetrievedThenReturn200() throws Exception { - int page = 0; - int size = 10; - - when(productApi.getProducts(page, size, null, null)).thenReturn(productList); - - mockMvc.perform(get(PRODUCTS_URL) - .param("page", String.valueOf(page)) - .param("size", String.valueOf(size)) - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.page").value(page)); - - verify(productApi).getProducts(page, size, null, null); - } - - private String buildErrorDescription(String className) { - final int problematicCodeLine = 33; - return String.format("Operation was failed in method: %s that belongs to the class: %s. Problematic code line: %d", - "getProductById", className, problematicCodeLine); - } - - private ApiResponse createExpectedErrorResponse(String errorDescription, UUID productId) { - return new ApiResponse<>( - null, - Collections.singletonList(String.format("The product with productId = %s is not found.", productId)), - errorDescription, - HttpStatus.NOT_FOUND.value(), - LocalDateTime.now()); - } + + @Test + void whenGetProductByIdThenReturn200() throws Exception { + UUID productId = UUID.randomUUID(); + + when(productApi.getProduct(productId)).thenReturn(productInfo); + + mockMvc.perform(get(PRODUCTS_URL + "/{productId}", productId) + .contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(productInfo.id().toString())); + + verify(productApi).getProduct(productId); + } + + @Test + void whenGetProductsThenReturn200() throws Exception { + int page = 1; + int size = 10; + String sortAttribute = "name"; + String defaultDirection = Sort.Direction.ASC.name(); + + when(productApi.getProducts(page, size, sortAttribute, defaultDirection)).thenReturn(productList); + + mockMvc.perform(get(PRODUCTS_URL) + .param("page", String.valueOf(page)) + .param("size", String.valueOf(size)) + .param("sort_attribute", sortAttribute) + .param("sort_direction", defaultDirection) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$.products.size()").value(productList.products().size())); + + verify(productApi).getProducts(page, size, sortAttribute, defaultDirection); + } + + @Test + void whenNullProductIdThenReturn404() throws Exception { + UUID productId = UUID.randomUUID(); + String errorDescription = buildErrorDescription( + ProductsEndpoint.class.getName() + ); + + when(productApi.getProduct(productId)).thenThrow(new ProductNotFoundException(productId)); + + MvcResult mvcResult = mockMvc.perform(get(PRODUCTS_URL + "/{productId}", productId)) + .andExpect(status().isNotFound()) + .andReturn(); + + String actualResponse = mvcResult.getResponse().getContentAsString(); + ApiResponse expectedResponse = createExpectedErrorResponse(errorDescription, productId); + String expectedResponseBody = objectMapper.writeValueAsString(expectedResponse); + + assertThat(actualResponse).isEqualTo(expectedResponseBody); + + verify(productApi).getProduct(productId); + } + + @Test + void whenGetProductsSortedByNameAscThenReturnProducts() throws Exception { + int page = 0; + int size = 10; + String sortAttribute = "name"; + Sort.Direction sortDirection = Sort.Direction.ASC; + + when(productApi.getProducts(page, size, sortAttribute, sortDirection.name())).thenReturn(productList); + + mockMvc.perform(get(PRODUCTS_URL) + .param("page", String.valueOf(page)) + .param("size", String.valueOf(size)) + .param("sort_attribute", sortAttribute) + .param("sort_direction", sortDirection.name()) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$.products[0].name").value("Product A")) + .andExpect(jsonPath("$.totalElements").value(productList.totalElements())); + + verify(productApi).getProducts(page, size, sortAttribute, sortDirection.name()); + } + + private String buildErrorDescription(String className) { + final int problematicCodeLine = 33; + return String.format("Operation was failed in method: %s that belongs to the class: %s. Problematic code line: %d", + "getProductById", className, problematicCodeLine); + } + + private ApiResponse createExpectedErrorResponse(String errorDescription, UUID productId) { + return new ApiResponse<>( + null, + Collections.singletonList(String.format("The product with productId = %s is not found.", productId)), + errorDescription, + HttpStatus.NOT_FOUND.value(), + LocalDateTime.now()); } } diff --git a/src/test/java/com/zufar/onlinestore/product/util/ProductUtilStub.java b/src/test/java/com/zufar/onlinestore/product/util/ProductStub.java similarity index 77% rename from src/test/java/com/zufar/onlinestore/product/util/ProductUtilStub.java rename to src/test/java/com/zufar/onlinestore/product/util/ProductStub.java index eb34900e..44c9298d 100644 --- a/src/test/java/com/zufar/onlinestore/product/util/ProductUtilStub.java +++ b/src/test/java/com/zufar/onlinestore/product/util/ProductStub.java @@ -8,9 +8,10 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.Map; import java.util.UUID; -public class ProductUtilStub { +public class ProductStub { public static ProductListWithPaginationInfoDto buildSampleProducts(Integer page, Integer size, String sortAttribute, Sort.Direction sortDirection) { List products = generateSampleProducts(); @@ -65,13 +66,18 @@ private static List generateSampleProducts() { } private static List sortProducts(List products, String sortAttribute, Sort.Direction direction) { - Comparator comparator = switch (sortAttribute.toLowerCase()) { - case "name" -> Comparator.comparing(ProductInfoDto::name); - case "description" -> Comparator.comparing(ProductInfoDto::description); - case "price" -> Comparator.comparing(ProductInfoDto::price); - case "quantity" -> Comparator.comparing(ProductInfoDto::quantity); - default -> throw new IllegalArgumentException("Unsupported sort attribute: " + sortAttribute); - }; + Map> attributeToComparator = Map.of( + "name", Comparator.comparing(ProductInfoDto::name), + "description", Comparator.comparing(ProductInfoDto::description), + "price", Comparator.comparing(ProductInfoDto::price), + "quantity", Comparator.comparing(ProductInfoDto::quantity) + ); + + Comparator comparator = attributeToComparator.getOrDefault(sortAttribute.toLowerCase(), null); + + if (comparator == null) { + throw new IllegalArgumentException("Unsupported sort attribute: " + sortAttribute); + } if (direction == Sort.Direction.DESC) { comparator = comparator.reversed(); @@ -80,6 +86,6 @@ private static List sortProducts(List products, } private static Integer calculateTotalPages(Integer totalElements, Integer size) { - return (int) Math.ceil((double) totalElements / size); + return (totalElements + size - 1) / size; } }