Skip to content

Commit

Permalink
test(product): add web layer tests for product package
Browse files Browse the repository at this point in the history
  • Loading branch information
korzhhiik committed Sep 16, 2023
1 parent b8bfee1 commit a151c7c
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 131 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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<Void> 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<Void> 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<Void> 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<Void> 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());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<ProductInfoDto> products = generateSampleProducts();
Expand Down Expand Up @@ -65,13 +66,18 @@ private static List<ProductInfoDto> generateSampleProducts() {
}

private static List<ProductInfoDto> sortProducts(List<ProductInfoDto> products, String sortAttribute, Sort.Direction direction) {
Comparator<ProductInfoDto> 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<String, Comparator<ProductInfoDto>> attributeToComparator = Map.of(
"name", Comparator.comparing(ProductInfoDto::name),
"description", Comparator.comparing(ProductInfoDto::description),
"price", Comparator.comparing(ProductInfoDto::price),
"quantity", Comparator.comparing(ProductInfoDto::quantity)
);

Comparator<ProductInfoDto> comparator = attributeToComparator.getOrDefault(sortAttribute.toLowerCase(), null);

if (comparator == null) {
throw new IllegalArgumentException("Unsupported sort attribute: " + sortAttribute);
}

if (direction == Sort.Direction.DESC) {
comparator = comparator.reversed();
Expand All @@ -80,6 +86,6 @@ private static List<ProductInfoDto> sortProducts(List<ProductInfoDto> products,
}

private static Integer calculateTotalPages(Integer totalElements, Integer size) {
return (int) Math.ceil((double) totalElements / size);
return (totalElements + size - 1) / size;
}
}

0 comments on commit a151c7c

Please sign in to comment.