-
Notifications
You must be signed in to change notification settings - Fork 0
Lab1
Detailed lab for implementing a Domain-Driven Design (DDD) project using Spring Boot. This lab focuses on creating a simple Book Management System with CRUD operations, using PostgreSQL as the database. Each step includes implementing the functionality and writing JUnit tests.
- Java Development Kit (JDK) 11 or later
- PostgreSQL installed and running
- An IDE that supports Spring, such as IntelliJ IDEA or Eclipse
- Maven for dependency management and project building
-
Create a new Spring Boot project using Spring Initializr:
- Choose Maven Project with Java.
- Add dependencies: Spring Web, Spring Data JPA, PostgreSQL Driver, Spring Boot Test.
- Download and open it in your IDE.
-
Configure application properties in
src/main/resources/application.properties:spring.datasource.url=jdbc:postgresql://localhost:5432/bookdb spring.datasource.username=postgres spring.datasource.password=your_password spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true
-
Create the Book entity in
src/main/java/com/example/library/domain/model/Book.java:package com.example.library.domain.model; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class Book { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String title; private String author; private String isbn; // Constructors, getters, and setters }
-
Create the BookRepository interface in
src/main/java/com/example/library/domain/repository/BookRepository.java:package com.example.library.domain.repository; import com.example.library.domain.model.Book; import org.springframework.data.jpa.repository.JpaRepository; public interface BookRepository extends JpaRepository<Book, Long> { }
-
Create the BookService class in
src/main/java/com/example/library/domain/service/BookService.java:package com.example.library.domain.service; import com.example.library.domain.model.Book; import com.example.library.domain.repository.BookRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Service public class BookService { private final BookRepository bookRepository; @Autowired public BookService(BookRepository bookRepository) { this.bookRepository = bookRepository; } public Book saveBook(Book book) { return bookRepository.save(book); } public List<Book> findAllBooks() { return bookRepository.findAll(); } public Book findBookById(Long id) { return bookRepository.findById(id).orElse(null); } public void deleteBook(Long id) { bookRepository.deleteById(id); } }
-
Create the BookController class in
src/main/java/com/example/library/application/BookController.java:package com.example.library.application; import com.example.library.domain.model.Book; import com.example.library.domain.service.BookService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/books") public class BookController { private final BookService bookService; @Autowired public BookController(BookService bookService) { this.bookService = bookService; } @PostMapping public Book addBook(@RequestBody Book book) { return bookService.saveBook(book); } @GetMapping public List<Book> getAllBooks() { return bookService.findAllBooks(); } @GetMapping("/{id}") public Book getBookById(@PathVariable Long id) { return bookService.findBookById(id); } @DeleteMapping("/{id}") public void deleteBook(@PathVariable Long id) { bookService.deleteBook(id); } }
-
Write unit tests in
src/test/java/com/example/library/domain/service/BookServiceTests.java:package com.example.library.domain.service; import com.example.library.domain.model.Book; import com.example.library.domain.repository.BookRepository; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.boot.test.context.SpringBootTest; import java.util.Optional; import static org.mockito.Mockito.*; import static org.junit.jupiter.api.Assertions.*; @SpringBootTest @ExtendWith(MockitoExtension.class) public class BookServiceTests { @Mock private BookRepository bookRepository; @InjectMocks private BookService bookService; @Test public void testSaveBook() { Book book = new Book("1984", "George Orwell", "1234567890"); when(bookRepository.save(any(Book.class))).thenReturn(book); Book savedBook = bookService.saveBook(book); assertNotNull(savedBook); assertEquals("1984", savedBook.getTitle()); } @Test public void testFindBookById() { Book book = new Book("1984", "George Orwell", "1234567890"); when(bookRepository.findById(anyLong())).thenReturn(Optional.of(book)); Book foundBook = bookService.findBookById(1L); assertNotNull(foundBook); assertEquals("1984", foundBook.getTitle()); } }
We'll start with more tests for the BookRepository, although usually, it might not need extensive testing if it extends Spring Data JPA's interfaces directly. However, for custom queries, it's good to test those.
package com.example.library.domain.repository;
import com.example.library.domain.model.Book;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import static org.assertj.core.api.Assertions.assertThat;
@DataJpaTest
public class BookRepositoryTests {
@Autowired
private BookRepository bookRepository;
@Test
void testSaveBook() {
Book book = new Book("Sapiens", "Yuval Noah Harari", "987654321");
book = bookRepository.save(book);
assertThat(book.getId()).isNotNull();
}
@Test
void testDeleteBook() {
Book book = new Book("Sapiens", "Yuval Noah Harari", "987654321");
book = bookRepository.save(book);
bookRepository.delete(book);
assertThat(bookRepository.findById(book.getId())).isEmpty();
}
}These tests use Mockito to mock the repository interactions and focus on the business logic in the service layer.
package com.example.library.domain.service;
import com.example.library.domain.model.Book;
import com.example.library.domain.repository.BookRepository;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Optional;
import java.util.ArrayList;
import java.util.List;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
@ExtendWith(MockitoExtension.class)
public class BookServiceTests {
@Mock
private BookRepository bookRepository;
@InjectMocks
private BookService bookService;
@Test
public void testGetAllBooks() {
List<Book> books = new ArrayList<>();
books.add(new Book("1984", "George Orwell", "1234567890"));
books.add(new Book("Brave New World", "Aldous Huxley", "0987654321"));
when(bookRepository.findAll()).thenReturn(books);
List<Book> foundBooks = bookService.findAllBooks();
assertNotNull(foundBooks);
assertEquals(2, foundBooks.size());
verify(bookRepository, times(1)).findAll();
}
@Test
public void testDeleteBookById() {
Long bookId = 1L;
doNothing().when(bookRepository).deleteById(bookId);
bookService.deleteBook(bookId);
verify(bookRepository).deleteById(bookId);
}
}The controller tests will mock the service layer and focus on HTTP handling and routing.
package com.example.library.application;
import com.example.library.domain.model.Book;
import com.example.library.domain.service.BookService;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.http.ResponseEntity;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@WebMvcTest(BookController.class)
public class BookControllerTests {
@Autowired
private MockMvc mockMvc;
@MockBean
private BookService bookService;
@Test
public void testGetBookById() throws Exception {
Book book = new Book("1984", "George Orwell", "1234567890");
given(bookService.findBookById(1L)).willReturn(book);
mockMvc.perform(get("/books/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.title").value("1984"))
.andExpect(jsonPath("$.author").value("George Orwell"))
.andExpect(jsonPath("$.isbn").value("1234567890"));
}
@Test
public void testAddBook() throws Exception {
Book book = new Book("1984", "George Orwell", "1234567890");
given(bookService.saveBook(any(Book.class))).willReturn(book);
mockMvc.perform(post("/books")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"title\":\"1984\",\"author\":\"George Orwell\",\"isbn\":\"1234567890\"}"))