Skip to content

Commit

Permalink
- Added test methods for BookController, BookService, and BookReposi…
Browse files Browse the repository at this point in the history
…tory.

 - Added test methods for CategoryController, CategoryService, and CategoryRepository.
  • Loading branch information
nklimovych committed May 29, 2024
1 parent e96271d commit 5eb62ec
Show file tree
Hide file tree
Showing 14 changed files with 871 additions and 294 deletions.
4 changes: 4 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-docker-compose</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
import mate.academy.bookstore.dto.book.BookSearchParametersDto;
import mate.academy.bookstore.dto.book.CreateBookRequestDto;
import mate.academy.bookstore.service.BookService;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
Expand All @@ -33,7 +35,9 @@ public class BookController {
@GetMapping
@ResponseStatus(HttpStatus.OK)
@Operation(summary = "Get all books", description = "Get a list of all available books")
public List<BookDto> getAll(Pageable pageable) {
public List<BookDto> getAll(@ParameterObject
@PageableDefault(sort = {"price", "title"}, value = 5)
Pageable pageable) {
return bookService.findAll(pageable);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import java.util.List;
import java.util.Optional;
import mate.academy.bookstore.model.Book;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
Expand All @@ -15,9 +17,15 @@ public interface BookRepository extends JpaRepository<Book, Long>, JpaSpecificat
@Query("FROM Book b JOIN FETCH b.categories")
List<Book> findAllBooks(Pageable pageable);

@EntityGraph(attributePaths = {"categories"})
Page<Book> findAll(Specification<Book> spec, Pageable pageable);

@EntityGraph(attributePaths = {"categories"})
Optional<Book> findBookByIsbn(String isbn);

@Query("FROM Book b JOIN FETCH b.categories WHERE b.id = :id")
Optional<Book> findBookById(Long id);

@Query("SELECT b FROM Book b LEFT JOIN FETCH b.categories c WHERE c.id = :categoryId")
List<Book> findAllBooksByCategoryId(@Param("categoryId") Long categoryId, Pageable pageable);
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@ public String getKey() {

@Override
public Specification<Book> getSpecification(BookSearchParametersDto params) {
int minPrice = Optional.ofNullable(params.minPrice()).orElse(DEFAULT_MIN_PRICE);
int maxPrice = Optional.ofNullable(params.maxPrice()).orElse(DEFAULT_MAX_PRICE);
int paramsMinPrice = Optional.ofNullable(params.minPrice()).orElse(DEFAULT_MIN_PRICE);
int paramsMaxPrice = Optional.ofNullable(params.maxPrice()).orElse(DEFAULT_MAX_PRICE);

int minPrice = Math.max(paramsMinPrice, DEFAULT_MIN_PRICE);
int maxPrice = (paramsMaxPrice > paramsMinPrice) ? paramsMaxPrice : DEFAULT_MAX_PRICE;

return ((root, query, criteriaBuilder) ->
criteriaBuilder.between(root.get(getKey()), minPrice, maxPrice));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public List<BookDto> findAll(Pageable pageable) {
}

public BookDto findById(Long id) {
Book book = bookRepository.findById(id).orElseThrow(
Book book = bookRepository.findBookById(id).orElseThrow(
() -> new EntityNotFoundException("Book not found by id " + id));
return bookMapper.toDto(book);
}
Expand Down
203 changes: 149 additions & 54 deletions src/test/java/mate/academy/bookstore/controller/BookControllerTest.java
Original file line number Diff line number Diff line change
@@ -1,74 +1,169 @@
package mate.academy.bookstore.controller;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.when;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import com.fasterxml.jackson.databind.ObjectMapper;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.Set;
import mate.academy.bookstore.dto.book.BookDto;
import mate.academy.bookstore.dto.book.BookSearchParametersDto;
import mate.academy.bookstore.dto.book.CreateBookRequestDto;
import mate.academy.bookstore.service.BookService;
import org.junit.jupiter.api.BeforeAll;
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.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.data.domain.Pageable;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class BookControllerTest {
private static final String ID = "/{id}";
private static final String TITLE_PARAM_NAME = "title";
private static final String AUTHOR_PARAM_NAME = "author";
private static final String ISBN_PARAM_NAME = "isbn";
private static final String PRICE_PARAM_NAME = "price";
private static final String BASE_URL = "/books";
private static final String SEARCH_URL = BASE_URL + "/search";
private static final String ADMIN_ROLE = "ADMIN";
private static final String USER_ROLE = "USER";
private static final String VALID_BOOK_TITLE = "Kobzar";
private static final String VALID_BOOK_AUTHOR = "Taras Shevchenko";
private static final String VALID_BOOK_ISBN = "978-0-7847-5628-7";
private static final BigDecimal VALID_BOOK_PRICE = BigDecimal.valueOf(14.99);
private static final Set<Long> VALID_CATEGORY_ID = Set.of(1L);
private static final Long VALID_BOOK_ID = 1L;
private static final String TITLE_0_EXPRESSION = "$[0].title";
private static final String TITLE_EXPRESSION = "$.title";

class BookControllerTest {
private static MockMvc mockMvc;

@Test
void getAll_ValidRequest_True() {
}

@Test
void getAll_InvalidRequest_False() {
}

@Test
void getBookById_ValidId_True() {
}

@Test
void getBookById_InvalidId_False() {
}

@Test
void getBookById_NullId_False() {
}

@Test
void createBook_ValidBook_True() {
}

@Test
void createBook_InvalidBook_False() {
}

@Test
void createBook_NullBook_False() {
}
@MockBean
private BookService bookService;

@Test
void updateBook_ValidBook_True() {
}

@Test
void updateBook_InvalidBook_False() {
}
@Autowired
private ObjectMapper objectMapper;

@Test
void updateBook_NullBook_False() {
}
private BookDto bookDto;
private CreateBookRequestDto createBookDto;

@BeforeAll
static void beforeAll(@Autowired WebApplicationContext context) {
mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
}

@BeforeEach
void setup() {
bookDto = new BookDto();
bookDto.setId(VALID_BOOK_ID);
bookDto.setTitle(VALID_BOOK_TITLE);
bookDto.setAuthor(VALID_BOOK_AUTHOR);
bookDto.setIsbn(VALID_BOOK_ISBN);
bookDto.setPrice(VALID_BOOK_PRICE);

createBookDto = new CreateBookRequestDto();
createBookDto.setTitle(VALID_BOOK_TITLE);
createBookDto.setAuthor(VALID_BOOK_AUTHOR);
createBookDto.setIsbn(VALID_BOOK_ISBN);
createBookDto.setCategoryIds(VALID_CATEGORY_ID);
createBookDto.setPrice(VALID_BOOK_PRICE);
}

@Test
@WithMockUser(roles = {USER_ROLE, ADMIN_ROLE})
@DisplayName("Get all books (valid request)")
void getAllBooks_ValidRequest_ReturnsListOfBooks() throws Exception {
when(bookService.findAll(any(Pageable.class))).thenReturn(
Collections.singletonList(bookDto));

mockMvc.perform(get(BASE_URL))
.andExpect(status().isOk())
.andExpect(jsonPath(TITLE_0_EXPRESSION).value(VALID_BOOK_TITLE));
}

@Test
@WithMockUser(roles = {USER_ROLE, ADMIN_ROLE})
@DisplayName("Get book by id (valid request)")
void getBookById_ValidRequest_ReturnsBookDto() throws Exception {
when(bookService.findById(anyLong())).thenReturn(bookDto);

mockMvc.perform(get(BASE_URL + ID, VALID_BOOK_ID))
.andExpect(status().isOk())
.andExpect(jsonPath(TITLE_EXPRESSION).value(VALID_BOOK_TITLE));
}

@Test
@WithMockUser(roles = ADMIN_ROLE)
@DisplayName("Create book (valid request)")
void createBook_ValidRequest_ReturnsCreatedBookDto() throws Exception {
when(bookService.save(any(CreateBookRequestDto.class))).thenReturn(bookDto);

String requestContent = objectMapper.writeValueAsString(createBookDto);

mockMvc.perform(post(BASE_URL)
.contentType(MediaType.APPLICATION_JSON)
.content(requestContent))
.andExpect(status().isCreated())
.andExpect(jsonPath(TITLE_EXPRESSION).value(VALID_BOOK_TITLE));
}

@Test
void deleteBook_ValidId_True() {
}
@WithMockUser(roles = ADMIN_ROLE)
@DisplayName("Update book (valid request)")
void updateBook_ValidRequest_ReturnsUpdatedBookDto() throws Exception {
when(bookService.updateById(anyLong(), any(CreateBookRequestDto.class))).thenReturn(
bookDto);

@Test
void deleteBook_InvalidId_False() {
}
String requestContent = objectMapper.writeValueAsString(createBookDto);

@Test
void deleteBook_NullId_False() {
mockMvc.perform(put(BASE_URL + ID, VALID_BOOK_ID)
.contentType(MediaType.APPLICATION_JSON)
.content(requestContent))
.andExpect(status().isAccepted())
.andExpect(jsonPath(TITLE_EXPRESSION).value(VALID_BOOK_TITLE));
}

@Test
void searchBook_ValidCriteria_True() {
@WithMockUser(roles = ADMIN_ROLE)
@DisplayName("Delete book (valid request)")
void deleteBook_ValidRequest_ReturnsNoContent() throws Exception {
mockMvc.perform(delete(BASE_URL + ID, VALID_BOOK_ID))
.andExpect(status().isNoContent());
}

@Test
void searchBook_InvalidCriteria_False() {
}
@WithMockUser(roles = {USER_ROLE, ADMIN_ROLE})
@DisplayName("Search books (valid request)")
void searchBooks_ValidRequest_ReturnsListOfBooks() throws Exception {
when(bookService.search(any(BookSearchParametersDto.class), any(Pageable.class)))
.thenReturn(Collections.singletonList(bookDto));

@Test
void searchBook_EmptyCriteria_False() {
mockMvc.perform(get(SEARCH_URL)
.param(TITLE_PARAM_NAME, VALID_BOOK_TITLE)
.param(AUTHOR_PARAM_NAME, VALID_BOOK_AUTHOR)
.param(ISBN_PARAM_NAME, VALID_BOOK_ISBN)
.param(PRICE_PARAM_NAME, VALID_BOOK_PRICE.toString()))
.andExpect(status().isOk())
.andExpect(jsonPath(TITLE_0_EXPRESSION).value(VALID_BOOK_TITLE));
}
}
Loading

0 comments on commit 5eb62ec

Please sign in to comment.