diff --git a/spring-web-modules/spring-boot-jsp/pom.xml b/spring-web-modules/spring-boot-jsp/pom.xml index 1599f4aa34..672cc8b426 100644 --- a/spring-web-modules/spring-boot-jsp/pom.xml +++ b/spring-web-modules/spring-boot-jsp/pom.xml @@ -37,7 +37,7 @@ org.apache.tomcat.embed tomcat-embed-jasper - + @@ -50,12 +50,29 @@ org.springframework.boot spring-boot-starter-web - + + + org.junit.jupiter + junit-jupiter-engine + ${junit-jupiter.version} + test + + + org.mockito + mockito-core + ${mockito.version} + test + + + org.springframework.boot + spring-boot-starter-test + test + @@ -80,7 +97,8 @@ 1.2 - 2.4.0 + 5.7.1 + 2.4.4 \ No newline at end of file diff --git a/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/SpringBootJspConfiguration.java b/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/SpringBootJspConfiguration.java index 83847a40a3..7fdae2c2a6 100644 --- a/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/SpringBootJspConfiguration.java +++ b/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/SpringBootJspConfiguration.java @@ -3,8 +3,8 @@ 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.ComponentScan; import org.springframework.context.annotation.Configuration; import com.baeldung.boot.jsp.repository.BookRepository; @@ -12,7 +12,6 @@ import com.baeldung.boot.jsp.repository.impl.InMemoryBookRepository; import com.baeldung.boot.jsp.repository.model.BookData; @Configuration -@ComponentScan(basePackages = "com.baeldung.boot.jsp") public class SpringBootJspConfiguration { @Bean @@ -22,9 +21,9 @@ public class SpringBootJspConfiguration { private static Map initialBookData() { Map 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")); + 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; } } \ No newline at end of file diff --git a/spring-web-modules/spring-boot-jsp/src/main/webapp/WEB-INF/jsp/add-book.jsp b/spring-web-modules/spring-boot-jsp/src/main/webapp/WEB-INF/jsp/add-book.jsp index 3b815dfafb..8195743da8 100644 --- a/spring-web-modules/spring-boot-jsp/src/main/webapp/WEB-INF/jsp/add-book.jsp +++ b/spring-web-modules/spring-boot-jsp/src/main/webapp/WEB-INF/jsp/add-book.jsp @@ -2,22 +2,20 @@ <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> - - Add Book - - + + Add Book + + + +
Successfully added Book with ISBN: ${savedBook.isbn}
+
- -
Successfully added Book with ISBN: ${savedBook.isbn}
-
- - - - ISBN: - Book Name: - Author Name: - - - - + + + ISBN: + Book Name: + Author Name: + + + \ No newline at end of file diff --git a/spring-web-modules/spring-boot-jsp/src/main/webapp/WEB-INF/jsp/error-book.jsp b/spring-web-modules/spring-boot-jsp/src/main/webapp/WEB-INF/jsp/error-book.jsp index c04756462d..6db90ca5c7 100644 --- a/spring-web-modules/spring-boot-jsp/src/main/webapp/WEB-INF/jsp/error-book.jsp +++ b/spring-web-modules/spring-boot-jsp/src/main/webapp/WEB-INF/jsp/error-book.jsp @@ -7,12 +7,12 @@ --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> - - Error - - -

Reference: ${ref}

-

Error Message: ${message}

-

Object: ${object}

- + + Error + + +

Reference: ${ref}

+

Error Message: ${message}

+

Object: ${object}

+ diff --git a/spring-web-modules/spring-boot-jsp/src/main/webapp/WEB-INF/jsp/view-books.jsp b/spring-web-modules/spring-boot-jsp/src/main/webapp/WEB-INF/jsp/view-books.jsp index 46bfbbac99..4a8e00a69b 100644 --- a/spring-web-modules/spring-boot-jsp/src/main/webapp/WEB-INF/jsp/view-books.jsp +++ b/spring-web-modules/spring-boot-jsp/src/main/webapp/WEB-INF/jsp/view-books.jsp @@ -1,30 +1,28 @@ <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> - - View Books - - " rel="stylesheet" type="text/css"> - - - - - - - - - - - - - + + View Books + " rel="stylesheet" type="text/css"> + + +
ISBNNameAuthor
+ - - - + + + - - -
${book.isbn}${book.name}${book.author}ISBNNameAuthor
- + + + + + ${book.isbn} + ${book.name} + ${book.author} + + + + + \ No newline at end of file diff --git a/spring-web-modules/spring-boot-jsp/src/test/java/com/baeldung/boot/jsp/controller/BookControllerIntegrationTest.java b/spring-web-modules/spring-boot-jsp/src/test/java/com/baeldung/boot/jsp/controller/BookControllerIntegrationTest.java new file mode 100644 index 0000000000..1847cbf545 --- /dev/null +++ b/spring-web-modules/spring-boot-jsp/src/test/java/com/baeldung/boot/jsp/controller/BookControllerIntegrationTest.java @@ -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 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()); + } + } +} \ No newline at end of file diff --git a/spring-web-modules/spring-boot-jsp/src/test/java/com/baeldung/boot/jsp/controller/BookControllerUnitTest.java b/spring-web-modules/spring-boot-jsp/src/test/java/com/baeldung/boot/jsp/controller/BookControllerUnitTest.java new file mode 100644 index 0000000000..af1d3d4956 --- /dev/null +++ b/spring-web-modules/spring-boot-jsp/src/test/java/com/baeldung/boot/jsp/controller/BookControllerUnitTest.java @@ -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 existingBooks() { + List 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; + } +} \ No newline at end of file diff --git a/spring-web-modules/spring-boot-jsp/src/test/java/com/baeldung/boot/jsp/repository/impl/InMemoryBookRepositoryUnitTest.java b/spring-web-modules/spring-boot-jsp/src/test/java/com/baeldung/boot/jsp/repository/impl/InMemoryBookRepositoryUnitTest.java new file mode 100644 index 0000000000..83f0c19e26 --- /dev/null +++ b/spring-web-modules/spring-boot-jsp/src/test/java/com/baeldung/boot/jsp/repository/impl/InMemoryBookRepositoryUnitTest.java @@ -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 storedBooks = bookRepository.findAll(); + + assertEquals(0, storedBooks.size()); + } + + @Test + public void givenInitialData_whenFindAll_thenReturnInitialData() { + BookRepository bookRepository = new InMemoryBookRepository(initialBookData()); + Collection storedBooks = bookRepository.findAll(); + + assertEquals(3, storedBooks.size()); + } + + @Test + public void givenInitialData_whenFindUnavailableIsbn_thenReturnEmpty() { + BookRepository bookRepository = new InMemoryBookRepository(initialBookData()); + Optional storedBookOpt = bookRepository.findById("isbn4"); + + assertFalse(storedBookOpt.isPresent()); + } + + @Test + public void givenInitialData_whenFindAvailableIsbn_thenReturnItem() { + BookRepository bookRepository = new InMemoryBookRepository(initialBookData()); + Optional 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 storedBookOpt = bookRepository.findById("isbn4"); + + assertTrue(storedBookOpt.isPresent()); + } + + private static Map initialBookData() { + Map 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; + } +} \ No newline at end of file diff --git a/spring-web-modules/spring-boot-jsp/src/test/java/com/baeldung/boot/jsp/service/BookServiceIntegrationTest.java b/spring-web-modules/spring-boot-jsp/src/test/java/com/baeldung/boot/jsp/service/BookServiceIntegrationTest.java new file mode 100644 index 0000000000..4223f3f970 --- /dev/null +++ b/spring-web-modules/spring-boot-jsp/src/test/java/com/baeldung/boot/jsp/service/BookServiceIntegrationTest.java @@ -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 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 initialBookData() { + Map 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; + } + } +} \ No newline at end of file diff --git a/spring-web-modules/spring-boot-jsp/src/test/java/com/baeldung/boot/jsp/service/impl/BookServiceImplUnitTest.java b/spring-web-modules/spring-boot-jsp/src/test/java/com/baeldung/boot/jsp/service/impl/BookServiceImplUnitTest.java new file mode 100644 index 0000000000..defbf71fd9 --- /dev/null +++ b/spring-web-modules/spring-boot-jsp/src/test/java/com/baeldung/boot/jsp/service/impl/BookServiceImplUnitTest.java @@ -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 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 existingBooks() { + List 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; + } +} \ No newline at end of file