Merge pull request #10553 from jason-m-s/BAEL-4810-spring-boot-with-jsp

BAEL-4810 Spring Boot with JSP
This commit is contained in:
Jonathan Cook 2021-04-02 13:16:40 +02:00 committed by GitHub
commit 0039f3c637
24 changed files with 839 additions and 0 deletions

View File

@ -41,6 +41,7 @@
<module>spring-thymeleaf</module>
<module>spring-thymeleaf-2</module>
<module>spring-thymeleaf-3</module>
<module>spring-boot-jsp</module>
</modules>
</project>

View File

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-boot-jsp</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-jsp</name>
<packaging>war</packaging>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>spring-web-modules</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>${jstl.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<!--uncomment below if deploying in web container-->
<!--<scope>provided</scope>-->
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--uncomment below if deploying in web container-->
<!--<dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-tomcat</artifactId>-->
<!-- <scope>provided</scope>-->
<!--</dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>spring-boot-jsp</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<mainClass>com.baeldung.boot.jsp.SpringBootJspApplication</mainClass>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<properties>
<jstl.version>1.2</jstl.version>
<junit-jupiter.version>5.7.1</junit-jupiter.version>
<spring-boot.version>2.4.4</spring-boot.version>
</properties>
</project>

View File

@ -0,0 +1,19 @@
package com.baeldung.boot.jsp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@SpringBootApplication(scanBasePackages = "com.baeldung.boot.jsp")
public class SpringBootJspApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(SpringBootJspApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(SpringBootJspApplication.class);
}
}

View File

@ -0,0 +1,29 @@
package com.baeldung.boot.jsp;
import java.util.HashMap;
import java.util.Map;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.baeldung.boot.jsp.repository.BookRepository;
import com.baeldung.boot.jsp.repository.impl.InMemoryBookRepository;
import com.baeldung.boot.jsp.repository.model.BookData;
@Configuration
public class SpringBootJspConfiguration {
@Bean
public BookRepository provideBookRepository() {
return new InMemoryBookRepository(initialBookData());
}
private static Map<String, BookData> initialBookData() {
Map<String, BookData> initData = new HashMap<>();
initData.put("ISBN-1", new BookData("ISBN-1", "Book 1", "Book 1 Author"));
initData.put("ISBN-2", new BookData("ISBN-2", "Book 2", "Book 2 Author"));
initData.put("ISBN-3", new BookData("ISBN-3", "Book 3", "Book 3 Author"));
return initData;
}
}

View File

@ -0,0 +1,45 @@
package com.baeldung.boot.jsp.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.view.RedirectView;
import com.baeldung.boot.jsp.dto.Book;
import com.baeldung.boot.jsp.service.BookService;
@Controller
@RequestMapping("/book")
public class BookController {
private final BookService bookService;
public BookController(BookService bookService) {
this.bookService = bookService;
}
@GetMapping("/viewBooks")
public String viewBooks(Model model) {
model.addAttribute("books", bookService.getBooks());
return "view-books";
}
@GetMapping("/addBook")
public String addBookView(Model model) {
model.addAttribute("book", new Book());
return "add-book";
}
@PostMapping("/addBook")
public RedirectView addBook(@ModelAttribute("book") Book book, RedirectAttributes redirectAttributes) {
final RedirectView redirectView = new RedirectView("/book/addBook", true);
Book savedBook = bookService.addBook(book);
redirectAttributes.addFlashAttribute("savedBook", savedBook);
redirectAttributes.addFlashAttribute("addBookSuccess", true);
return redirectView;
}
}

View File

@ -0,0 +1,14 @@
package com.baeldung.boot.jsp.dto;
import lombok.*;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Book {
private String isbn;
private String name;
private String author;
}

View File

@ -0,0 +1,13 @@
package com.baeldung.boot.jsp.exception;
import com.baeldung.boot.jsp.dto.Book;
import lombok.Getter;
@Getter
public class DuplicateBookException extends RuntimeException {
private final Book book;
public DuplicateBookException(Book book) {
this.book = book;
}
}

View File

@ -0,0 +1,19 @@
package com.baeldung.boot.jsp.exception;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
@ControllerAdvice
public class LibraryControllerAdvice {
@ExceptionHandler(value = DuplicateBookException.class)
public ModelAndView duplicateBookException(DuplicateBookException e) {
final ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("ref", e.getBook().getIsbn());
modelAndView.addObject("object", e.getBook());
modelAndView.addObject("message", "Cannot add an already existing book");
modelAndView.setViewName("error-book");
return modelAndView;
}
}

View File

@ -0,0 +1,14 @@
package com.baeldung.boot.jsp.repository;
import java.util.Collection;
import java.util.Optional;
import com.baeldung.boot.jsp.repository.model.BookData;
public interface BookRepository {
Collection<BookData> findAll();
Optional<BookData> findById(String isbn);
BookData add(BookData book);
}

View File

@ -0,0 +1,36 @@
package com.baeldung.boot.jsp.repository.impl;
import java.util.*;
import com.baeldung.boot.jsp.repository.BookRepository;
import com.baeldung.boot.jsp.repository.model.BookData;
public class InMemoryBookRepository implements BookRepository {
private final Map<String, BookData> storedBooks;
public InMemoryBookRepository(Map<String, BookData> storedBooks) {
this.storedBooks = new HashMap<>();
this.storedBooks.putAll(storedBooks);
}
@Override
public Collection<BookData> findAll() {
if (storedBooks.isEmpty()) {
return Collections.emptyList();
}
return storedBooks.values();
}
@Override
public Optional<BookData> findById(String isbn) {
return Optional.ofNullable(storedBooks.get(isbn));
}
@Override
public BookData add(BookData book) {
storedBooks.put(book.getIsbn(), book);
return book;
}
}

View File

@ -0,0 +1,16 @@
package com.baeldung.boot.jsp.repository.model;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class BookData {
private String isbn;
private String name;
private String author;
}

View File

@ -0,0 +1,11 @@
package com.baeldung.boot.jsp.service;
import java.util.Collection;
import com.baeldung.boot.jsp.dto.Book;
public interface BookService {
Collection<Book> getBooks();
Book addBook(Book book);
}

View File

@ -0,0 +1,50 @@
package com.baeldung.boot.jsp.service.impl;
import java.util.Collection;
import java.util.Optional;
import java.util.stream.Collectors;
import com.baeldung.boot.jsp.exception.DuplicateBookException;
import org.springframework.stereotype.Service;
import com.baeldung.boot.jsp.dto.Book;
import com.baeldung.boot.jsp.repository.BookRepository;
import com.baeldung.boot.jsp.repository.model.BookData;
import com.baeldung.boot.jsp.service.BookService;
@Service
public class BookServiceImpl implements BookService {
private final BookRepository bookRepository;
public BookServiceImpl(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
@Override
public Collection<Book> getBooks() {
return bookRepository.findAll()
.stream()
.map(BookServiceImpl::convertBookData)
.collect(Collectors.toList());
}
@Override
public Book addBook(Book book) {
final Optional<BookData> existingBook = bookRepository.findById(book.getIsbn());
if (existingBook.isPresent()) {
throw new DuplicateBookException(book);
}
final BookData savedBook = bookRepository.add(convertBook(book));
return convertBookData(savedBook);
}
private static Book convertBookData(BookData bookData) {
return new Book(bookData.getIsbn(), bookData.getName(), bookData.getAuthor());
}
private static BookData convertBook(Book book) {
return new BookData(book.getIsbn(), book.getName(), book.getAuthor());
}
}

View File

@ -0,0 +1,4 @@
server.servlet.context-path=/spring-boot-jsp
spring.mvc.view.prefix: /WEB-INF/jsp/
spring.mvc.view.suffix: .jsp

View File

@ -0,0 +1,10 @@
table {
font-family: arial, sans-serif;
border-collapse: collapse;
}
td, th {
border: 1px solid #dddddd;
text-align: left;
padding: 8px;
}

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Error</title>
</head>
<body>
Opps! 4xx Error Occurred.
</body>
</html>

View File

@ -0,0 +1,21 @@
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Add Book</title>
</head>
<body>
<c:if test="${addBookSuccess}">
<div>Successfully added Book with ISBN: ${savedBook.isbn}</div>
</c:if>
<c:url var="add_book_url" value="/book/addBook"/>
<form:form action="${add_book_url}" method="post" modelAttribute="book">
<form:label path="isbn">ISBN: </form:label> <form:input type="text" path="isbn"/>
<form:label path="name">Book Name: </form:label> <form:input type="text" path="name"/>
<form:label path="author">Author Name: </form:label> <form:input path="author"/>
<input type="submit" value="submit"/>
</form:form>
</body>
</html>

View File

@ -0,0 +1,18 @@
<%--
Created by IntelliJ IDEA.
User: jason
Date: 3/13/21
Time: 10:39 PM
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Error</title>
</head>
<body>
<p>Reference: ${ref}</p>
<p>Error Message: ${message}</p>
<p>Object: ${object}</p>
</body>
</html>

View File

@ -0,0 +1,28 @@
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<title>View Books</title>
<link href="<c:url value="/css/common.css"/>" rel="stylesheet" type="text/css">
</head>
<body>
<table>
<thead>
<tr>
<th>ISBN</th>
<th>Name</th>
<th>Author</th>
</tr>
</thead>
<tbody>
<c:forEach items="${books}" var="book">
<tr>
<td>${book.isbn}</td>
<td>${book.name}</td>
<td>${book.author}</td>
</tr>
</c:forEach>
</tbody>
</table>
</body>
</html>

View File

@ -0,0 +1,93 @@
package com.baeldung.boot.jsp.controller;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasProperty;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import java.util.Collections;
import java.util.Optional;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import com.baeldung.boot.jsp.repository.BookRepository;
import com.baeldung.boot.jsp.repository.impl.InMemoryBookRepository;
import com.baeldung.boot.jsp.repository.model.BookData;
@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextConfiguration
@AutoConfigureMockMvc
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class BookControllerIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private BookRepository bookRepository;
@Test
@Order(1)
public void whenAddBook_thenBookSaved() throws Exception {
MockHttpServletRequestBuilder addBookRequest = MockMvcRequestBuilders.post("/book/addBook")
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.param("isbn", "isbn1")
.param("name", "name1")
.param("author", "author1");
mockMvc.perform(addBookRequest)
.andReturn();
Optional<BookData> storedBookOpt = bookRepository.findById("isbn1");
assertTrue(storedBookOpt.isPresent());
assertEquals("name1", storedBookOpt.get()
.getName());
assertEquals("author1", storedBookOpt.get()
.getAuthor());
}
@Test
@Order(2)
public void givenAlreadyExistingBook_whenAddBook_thenShowErrorPage() throws Exception {
MockHttpServletRequestBuilder addBookRequest = MockMvcRequestBuilders.post("/book/addBook")
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.param("isbn", "isbn1")
.param("name", "name1")
.param("author", "author1");
ResultActions addBookResult = mockMvc.perform(addBookRequest);
addBookResult.andExpect(view().name("error-book"))
.andExpect(model().attribute("ref", "isbn1"))
.andExpect(model().attribute("object", hasProperty("isbn", equalTo("isbn1"))))
.andExpect(model().attribute("message", "Cannot add an already existing book"));
}
@Configuration
@ComponentScan("com.baeldung.boot.jsp")
static class ContextConfiguration {
@Bean
public BookRepository provideBookRepository() {
return new InMemoryBookRepository(Collections.emptyMap());
}
}
}

View File

@ -0,0 +1,76 @@
package com.baeldung.boot.jsp.controller;
import static org.hamcrest.Matchers.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.mockito.AdditionalAnswers;
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 org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import com.baeldung.boot.jsp.dto.Book;
import com.baeldung.boot.jsp.service.BookService;
@WebMvcTest(BookController.class)
class BookControllerUnitTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private BookService bookService;
@Test
public void whenViewBooks_thenReturnBooksView() throws Exception {
when(bookService.getBooks()).thenReturn(existingBooks());
ResultActions viewBooksResult = mockMvc.perform(get("/book/viewBooks"));
viewBooksResult.andExpect(view().name("view-books"))
.andExpect(model().attribute("books", hasSize(3)));
}
@Test
public void whenAddBookView_thenReturnAddBooksView() throws Exception {
ResultActions addBookViewResult = mockMvc.perform(get("/book/addBook"));
addBookViewResult.andExpect(view().name("add-book"))
.andExpect(model().attribute("book", isA(Book.class)));
}
@Test
public void whenAddBookPost_thenRedirectToAddBookView() throws Exception {
when(bookService.addBook(any(Book.class))).thenAnswer(AdditionalAnswers.returnsFirstArg());
MockHttpServletRequestBuilder addBookRequest = MockMvcRequestBuilders.post("/book/addBook")
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.param("isbn", "isbn1")
.param("name", "name1")
.param("author", "author1");
ResultActions addBookResult = mockMvc.perform(addBookRequest);
addBookResult.andExpect(status().is3xxRedirection())
.andExpect(redirectedUrl("/book/addBook"))
.andExpect(flash().attribute("savedBook", hasProperty("isbn", equalTo("isbn1"))))
.andExpect(flash().attribute("addBookSuccess", true));
}
private static Collection<Book> existingBooks() {
List<Book> books = new ArrayList<>();
books.add(new Book("isbn1", "name1", "author1"));
books.add(new Book("isbn2", "name2", "author2"));
books.add(new Book("isbn3", "name3", "author3"));
return books;
}
}

View File

@ -0,0 +1,62 @@
package com.baeldung.boot.jsp.repository.impl;
import static org.junit.jupiter.api.Assertions.*;
import java.util.*;
import org.junit.jupiter.api.Test;
import com.baeldung.boot.jsp.repository.BookRepository;
import com.baeldung.boot.jsp.repository.model.BookData;
public class InMemoryBookRepositoryUnitTest {
@Test
public void givenEmtpyData_whenFindAll_thenReturnEmptyCollection() {
BookRepository bookRepository = new InMemoryBookRepository(Collections.emptyMap());
Collection<BookData> storedBooks = bookRepository.findAll();
assertEquals(0, storedBooks.size());
}
@Test
public void givenInitialData_whenFindAll_thenReturnInitialData() {
BookRepository bookRepository = new InMemoryBookRepository(initialBookData());
Collection<BookData> storedBooks = bookRepository.findAll();
assertEquals(3, storedBooks.size());
}
@Test
public void givenInitialData_whenFindUnavailableIsbn_thenReturnEmpty() {
BookRepository bookRepository = new InMemoryBookRepository(initialBookData());
Optional<BookData> storedBookOpt = bookRepository.findById("isbn4");
assertFalse(storedBookOpt.isPresent());
}
@Test
public void givenInitialData_whenFindAvailableIsbn_thenReturnItem() {
BookRepository bookRepository = new InMemoryBookRepository(initialBookData());
Optional<BookData> storedBookOpt = bookRepository.findById("isbn1");
assertTrue(storedBookOpt.isPresent());
}
@Test
public void givenAddedIsbn_whenFindAvailableIsbn_thenReturnItem() {
BookRepository bookRepository = new InMemoryBookRepository(Collections.emptyMap());
bookRepository.add(new BookData("isbn4", "name4", "author4"));
Optional<BookData> storedBookOpt = bookRepository.findById("isbn4");
assertTrue(storedBookOpt.isPresent());
}
private static Map<String, BookData> initialBookData() {
Map<String, BookData> initData = new HashMap<>();
initData.put("isbn1", new BookData("isbn1", "name1", "author1"));
initData.put("isbn2", new BookData("isbn2", "name2", "author2"));
initData.put("isbn3", new BookData("isbn3", "name3", "author3"));
return initData;
}
}

View File

@ -0,0 +1,84 @@
package com.baeldung.boot.jsp.service;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import com.baeldung.boot.jsp.dto.Book;
import com.baeldung.boot.jsp.exception.DuplicateBookException;
import com.baeldung.boot.jsp.repository.BookRepository;
import com.baeldung.boot.jsp.repository.impl.InMemoryBookRepository;
import com.baeldung.boot.jsp.repository.model.BookData;
@ExtendWith(SpringExtension.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
public class BookServiceIntegrationTest {
@Autowired
private BookService bookService;
@Test
@Order(1)
public void givenNoAddedBooks_whenGetAllBooks_thenReturnInitialBooks() {
Collection<Book> storedBooks = bookService.getBooks();
assertEquals(3, storedBooks.size());
assertThat(storedBooks, hasItem(hasProperty("isbn", equalTo("ISBN-TEST-1"))));
assertThat(storedBooks, hasItem(hasProperty("isbn", equalTo("ISBN-TEST-2"))));
assertThat(storedBooks, hasItem(hasProperty("isbn", equalTo("ISBN-TEST-3"))));
}
@Test
@Order(2)
public void givenBookNotAlreadyExists_whenAddBook_thenReturnSuccessfully() {
Book bookToBeAdded = new Book("ISBN-ADD-TEST-4", "Added Book 4", "Added Book 4 Author");
Book storedBook = bookService.addBook(bookToBeAdded);
assertEquals(bookToBeAdded.getIsbn(), storedBook.getIsbn());
}
@Test
@Order(3)
public void givenBookAlreadyExists_whenAddBook_thenDuplicateBookException() {
Book bookToBeAdded = new Book("ISBN-ADD-TEST-4", "Updated Book 4", "Updated Book 4 Author");
assertThrows(DuplicateBookException.class, () -> bookService.addBook(bookToBeAdded));
}
@Configuration
@ComponentScan("com.baeldung.boot.jsp")
static class ContextConfiguration {
@Bean
public BookRepository provideBookRepository() {
return new InMemoryBookRepository(initialBookData());
}
private static Map<String, BookData> initialBookData() {
Map<String, BookData> initData = new HashMap<>();
initData.put("ISBN-TEST-1", new BookData("ISBN-TEST-1", "Book 1", "Book 1 Author"));
initData.put("ISBN-TEST-2", new BookData("ISBN-TEST-2", "Book 2", "Book 2 Author"));
initData.put("ISBN-TEST-3", new BookData("ISBN-TEST-3", "Book 3", "Book 3 Author"));
return initData;
}
}
}

View File

@ -0,0 +1,75 @@
package com.baeldung.boot.jsp.service.impl;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.AdditionalAnswers;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import com.baeldung.boot.jsp.dto.Book;
import com.baeldung.boot.jsp.exception.DuplicateBookException;
import com.baeldung.boot.jsp.repository.BookRepository;
import com.baeldung.boot.jsp.repository.model.BookData;
import com.baeldung.boot.jsp.service.BookService;
@ExtendWith(MockitoExtension.class)
public class BookServiceImplUnitTest {
@Mock
private BookRepository bookRepository;
@Test
public void whenGetBooks_thenAllBooksReturned() {
when(bookRepository.findAll()).thenReturn(existingBooks());
BookService bookService = new BookServiceImpl(bookRepository);
Collection<Book> storedBooks = bookService.getBooks();
assertEquals(3, storedBooks.size());
assertThat(storedBooks, hasItem(hasProperty("isbn", equalTo("isbn1"))));
assertThat(storedBooks, hasItem(hasProperty("isbn", equalTo("isbn2"))));
assertThat(storedBooks, hasItem(hasProperty("isbn", equalTo("isbn3"))));
}
@Test
public void whenAddBook_thenAddSuccessful() {
when(bookRepository.findById(anyString())).thenReturn(Optional.empty());
when(bookRepository.add(any(BookData.class))).thenAnswer(AdditionalAnswers.returnsFirstArg());
BookService bookService = new BookServiceImpl(bookRepository);
Book book = bookService.addBook(new Book("isbn1", "name1", "author1"));
assertEquals("isbn1", book.getIsbn());
assertEquals("name1", book.getName());
assertEquals("author1", book.getAuthor());
}
@Test
public void givenBookAlreadyExist_whenAddBook_thenDuplicateBookException() {
BookData existingBook = new BookData("isbn1", "name1", "author1");
when(bookRepository.findById("isbn1")).thenReturn(Optional.of(existingBook));
BookService bookService = new BookServiceImpl(bookRepository);
Book bookToBeAdded = new Book("isbn1", "name1", "author1");
assertThrows(DuplicateBookException.class, () -> bookService.addBook(bookToBeAdded));
}
private static Collection<BookData> existingBooks() {
List<BookData> books = new ArrayList<>();
books.add(new BookData("isbn1", "name1", "author1"));
books.add(new BookData("isbn2", "name2", "author2"));
books.add(new BookData("isbn3", "name3", "author3"));
return books;
}
}