From bf5caa68ea38b90a4147c9f1b57aa8e16cbffe36 Mon Sep 17 00:00:00 2001 From: Badri Paudel Date: Sat, 16 Mar 2024 21:00:34 +0545 Subject: [PATCH] Unit tests for user service (find user by username) and contact service (find contacts by username) written along with some other minor refactoring. --- .../keeper/controllers/AdminController.java | 1 + .../keeper/controllers/ContactController.java | 13 ++- .../info/keeper/service/ContactService.java | 13 ++- .../java/info/keeper/service/UserService.java | 20 +++++ .../controllers/HomeControllerTest.java | 3 + .../keeper/service/ContactServiceTest.java | 88 +++++++++++++++++++ .../info/keeper/service/UserServiceTest.java | 76 ++++++++++++++++ 7 files changed, 206 insertions(+), 8 deletions(-) create mode 100644 src/main/java/info/keeper/service/UserService.java create mode 100644 src/test/java/info/keeper/service/ContactServiceTest.java create mode 100644 src/test/java/info/keeper/service/UserServiceTest.java diff --git a/src/main/java/info/keeper/controllers/AdminController.java b/src/main/java/info/keeper/controllers/AdminController.java index 4fb2afa..f83706b 100644 --- a/src/main/java/info/keeper/controllers/AdminController.java +++ b/src/main/java/info/keeper/controllers/AdminController.java @@ -27,6 +27,7 @@ public AdminController(AdminRepository adminRepository, UserRepository userRepos @GetMapping("/index") public String showAdminPage(Principal principal, Model model) { + // In Prod app, put all the business login inside of the service and just use that in the controller. User user = userRepository.findUserByUsername(principal.getName()); model.addAttribute("user", user); model.addAttribute("adminMessage", new AdminMessage()); diff --git a/src/main/java/info/keeper/controllers/ContactController.java b/src/main/java/info/keeper/controllers/ContactController.java index 2a13468..0d28b82 100644 --- a/src/main/java/info/keeper/controllers/ContactController.java +++ b/src/main/java/info/keeper/controllers/ContactController.java @@ -5,10 +5,10 @@ import info.keeper.repositories.ContactRepository; import info.keeper.repositories.UserRepository; import info.keeper.service.ContactService; +import info.keeper.service.UserService; import info.keeper.utils.Message; import org.springframework.core.io.ClassPathResource; import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; @@ -34,11 +34,13 @@ public class ContactController { private final UserRepository userRepository; private final ContactRepository contactRepository; private final ContactService contactService; + private final UserService userService; - public ContactController(UserRepository userRepository, ContactRepository contactRepository, ContactService contactService) { + public ContactController(UserRepository userRepository, ContactRepository contactRepository, ContactService contactService, UserService userService) { this.userRepository = userRepository; this.contactRepository = contactRepository; this.contactService = contactService; + this.userService = userService; } // show add Note form @@ -108,11 +110,8 @@ public String addContact(@Valid @ModelAttribute("contact") Contact contact, Bind public String findContactsByUser(@PathVariable("pageNumber") int pageNumber, Pageable pageable, Model model, Principal principal) { - User user = userRepository.findUserByUsername(principal.getName()); - - //Pageable has current page and number of notes per page - Pageable pageable1 = PageRequest.of(pageNumber, 5); // 5 notes per page - Page allContacts = contactRepository.findContactsByUser(user.getId(), pageable1); + User user = userService.getUserByUsername(principal.getName()); + Page allContacts = contactService.findContactsByUser(principal.getName(), pageNumber); model.addAttribute("title", "All Of your Notes || Information Keeper"); model.addAttribute("user", user); diff --git a/src/main/java/info/keeper/service/ContactService.java b/src/main/java/info/keeper/service/ContactService.java index 661c48b..253e423 100644 --- a/src/main/java/info/keeper/service/ContactService.java +++ b/src/main/java/info/keeper/service/ContactService.java @@ -7,6 +7,9 @@ import info.keeper.utils.CsvUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; @@ -38,5 +41,13 @@ public boolean saveAllImportedContactsToDatabase( MultipartFile file, Principal logger.error("Something went wrong while importing contacts : "); return false; } - } + } + + public Page findContactsByUser(String username, int pageNumber ) { + User user = userRepository.findUserByUsername(username); + //Pageable has current page and number of notes per page + Pageable pageable = PageRequest.of(pageNumber, 5); // 5 notes per page + Page allContacts = contactRepository.findContactsByUser(user.getId(), pageable); + return allContacts; + } } diff --git a/src/main/java/info/keeper/service/UserService.java b/src/main/java/info/keeper/service/UserService.java new file mode 100644 index 0000000..3aca6fc --- /dev/null +++ b/src/main/java/info/keeper/service/UserService.java @@ -0,0 +1,20 @@ +package info.keeper.service; + +import info.keeper.models.User; +import info.keeper.repositories.UserRepository; +import org.springframework.stereotype.Service; + +@Service +public class UserService { + private UserRepository userRepository; + + public UserService(UserRepository userRepository) { + this.userRepository = userRepository; + } + + public User getUserByUsername(String username) { + User user = userRepository.findUserByUsername(username); + return user; + } +} + diff --git a/src/test/java/info/keeper/controllers/HomeControllerTest.java b/src/test/java/info/keeper/controllers/HomeControllerTest.java index 86fb5d8..cbd5795 100644 --- a/src/test/java/info/keeper/controllers/HomeControllerTest.java +++ b/src/test/java/info/keeper/controllers/HomeControllerTest.java @@ -3,6 +3,7 @@ import info.keeper.repositories.UserRepository; import org.junit.jupiter.api.AfterEach; 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.autoconfigure.web.servlet.WebMvcTest; @@ -61,6 +62,7 @@ void tearDown() { * More the coverage, the better it will be. */ @Test + @DisplayName("Ensure that GET: / returns the expected response with status and title of the page") void testHomePage() throws Exception { assertThat(homeController).isNotNull(); // assert homeController != null; // => same as above @@ -71,6 +73,7 @@ void testHomePage() throws Exception { @Test void testLoginPage() { + } @Test diff --git a/src/test/java/info/keeper/service/ContactServiceTest.java b/src/test/java/info/keeper/service/ContactServiceTest.java new file mode 100644 index 0000000..82e2825 --- /dev/null +++ b/src/test/java/info/keeper/service/ContactServiceTest.java @@ -0,0 +1,88 @@ +package info.keeper.service; + +import info.keeper.models.Contact; +import info.keeper.models.User; +import info.keeper.repositories.ContactRepository; +import info.keeper.repositories.UserRepository; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.ArrayList; +import java.util.List; + +@SpringBootTest +class ContactServiceTest { + @Mock + private ContactRepository contactRepository; + @Mock + private UserRepository userRepository; + + @InjectMocks + private ContactService serviceUnderTest; + + private User user; + private Integer userId; + private String username; + private Integer pageNumber; + private Long expectedNumberOfContacts; + + @BeforeEach + void setUp() { + // Any setup before each test. + username = "john.doe@gmail.com"; + userId = 101; + pageNumber = 1; + expectedNumberOfContacts = 5L; + user = new User(); + user.setEmail(username); + user.setId(userId); + } + @AfterEach + void cleanUp() { + // Any cleanup after each test. + } + + @Test + void findContactsByUser() { + Mockito.when(userRepository.findUserByUsername(username)).thenReturn(user); + Pageable pageable = PageRequest.of(pageNumber, 5); + Mockito.when(contactRepository.findContactsByUser(userId, pageable)).thenReturn(getContactsWithPaginationForUser()); + Page allContacts = serviceUnderTest.findContactsByUser(username, pageNumber); + assertThat(allContacts.getTotalElements()).isEqualTo(expectedNumberOfContacts); + Assertions.assertAll("Assert that returned elements have My Contact Name in name and 123456789 in phone number", + () -> { + allContacts.getContent().forEach( contact -> { + Assertions.assertTrue(contact.getName().contains("My Contact Name") && + contact.getPhoneNumber().contains("123456789")); + }); + } + ); + } + + private Page getContactsWithPaginationForUser() { + List contacts = new ArrayList<>(); + for (int i = 1; i <= 5; i++) { + String contactName = "My Contact Name - " + i; + String phoneNumber = "123456789" + i; + Contact contact = new Contact(); + contact.setId(i); + contact.setName(contactName); + contact.setPhoneNumber(phoneNumber); + contacts.add(contact); + } + // Create a mock Page object containing the contacts + PageImpl pagedContacts = new PageImpl<>(contacts); + return pagedContacts; + } +} \ No newline at end of file diff --git a/src/test/java/info/keeper/service/UserServiceTest.java b/src/test/java/info/keeper/service/UserServiceTest.java new file mode 100644 index 0000000..b12f6ed --- /dev/null +++ b/src/test/java/info/keeper/service/UserServiceTest.java @@ -0,0 +1,76 @@ +package info.keeper.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; + +import info.keeper.models.User; +import info.keeper.repositories.UserRepository; +import org.junit.jupiter.api.*; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.boot.test.context.SpringBootTest; + +/** + * REF: https://www.baeldung.com/java-spring-mockito-mock-mockbean (Mockito.Mock vs @Mock syntax) + * REF : https://stackoverflow.com/questions/16467685/difference-between-mock-and-injectmocks (@InjectMocks and @Mock) + */ +@SpringBootTest +class UserServiceTest { + // This UserRepository will be mocked (for some method of it, we will be returning the result of the mock and not the real db call) + @Mock + private UserRepository userRepository; + + /** + * Mark a field on which injection should be performed. + * @InjectMocks creates the instance of the class and injects the mocks that are + * created with the @Mock (or @Spy) annotations into this instance. + * REF: https://stackoverflow.com/questions/16467685/difference-between-mock-and-injectmocks + */ + @InjectMocks + private UserService userService; // Service under test, all services ( or repo ..) with Mock annotations applied will be injected into it. + + // Define fields. + private static User user; + private static String username; + + @BeforeAll + static void setUpData() { + username = "badripaudel77@gmail.com"; + user = new User(); + user.setEmail(username); + } + + @BeforeEach + void setUp() { + // Any setup before each test. + } + @AfterEach + void cleanUp() { + // Any cleanup after each test. + } + + @Test + @DisplayName("Test that getUserByUsername() of the service returns correct user.") + void testGetUserByUsername() { + /** + * Define mock behavior for userRepository + * That is, when user userRepository.findUserByUsername() is called, return the user (mock it, instead of calling the db) + */ + when(userRepository.findUserByUsername(username)).thenReturn(user); + // Mockito.when(userRepository.findUserByUsername(username)).thenReturn(user); + // Call the method under test + User userByUsername = userService.getUserByUsername(username); + + // Verify that the userRepository method was called with the correct parameter + verify(userRepository).findUserByUsername("badripaudel77@gmail.com"); + + // Verify that the returned user is the same as the expected user + assertThat(userByUsername).isEqualTo(user); + assert userByUsername.getEmail().equals(user.getEmail()) == true; + } + + @AfterAll + static void tearDown() { + // Any cleanup after all the tests are executed. + } +}