diff --git a/src/main/java/org/springframework/samples/petclinic/common/CommonAttribute.java b/src/main/java/org/springframework/samples/petclinic/common/CommonAttribute.java index 4fe322fd1..c3334533d 100644 --- a/src/main/java/org/springframework/samples/petclinic/common/CommonAttribute.java +++ b/src/main/java/org/springframework/samples/petclinic/common/CommonAttribute.java @@ -43,6 +43,14 @@ public final class CommonAttribute { public static final String VISIT = "visit"; + public static final String VISIT_DATE = "date"; + + public static final String VISIT_DESCRIPTION = "description"; + + public static final String VISIT_PET_ID = "petId"; + + public static final String VISITS = "visits"; + private CommonAttribute() { throw new IllegalStateException("Utility class"); } diff --git a/src/main/java/org/springframework/samples/petclinic/controller/PetController.java b/src/main/java/org/springframework/samples/petclinic/controller/PetController.java index 17f11a3c4..fa7aed54e 100644 --- a/src/main/java/org/springframework/samples/petclinic/controller/PetController.java +++ b/src/main/java/org/springframework/samples/petclinic/controller/PetController.java @@ -47,7 +47,6 @@ class PetController { private final PetService petService; - @Autowired PetController(OwnerService ownerService, PetService petService, PetTypeService petTypeService) { this.ownerService = ownerService; @@ -87,14 +86,14 @@ class PetController { @ModelAttribute(CommonAttribute.PET) @Valid PetDTO pet, BindingResult result, ModelMap model) { if (owner == null) { result.rejectValue(CommonAttribute.OWNER, CommonError.NOT_FOUND_ARGS, CommonError.NOT_FOUND_MESSAGE); - } else { + } + else { if (StringUtils.hasLength(pet.getName()) && pet.isNew() && owner.getPet(pet.getName(), true) != null) { result.rejectValue(CommonAttribute.NAME, CommonError.DUPLICATE_ARGS, CommonError.DUPLICATE_MESSAGE); } owner.addPet(pet); } - if (result.hasErrors()) { model.put(CommonAttribute.PET, pet); return CommonView.PET_CREATE_OR_UPDATE; diff --git a/src/main/java/org/springframework/samples/petclinic/controller/VetController.java b/src/main/java/org/springframework/samples/petclinic/controller/VetController.java index 64f00ca2f..1cb496e31 100644 --- a/src/main/java/org/springframework/samples/petclinic/controller/VetController.java +++ b/src/main/java/org/springframework/samples/petclinic/controller/VetController.java @@ -52,7 +52,7 @@ class VetController { return CommonView.VET_VETS_LIST; } - @GetMapping({ CommonEndPoint.VETS }) + @GetMapping(CommonEndPoint.VETS) public @ResponseBody VetsDTO showResourcesVetList() { // Here we are returning an object of type 'Vets' rather than a collection of Vet // objects so it is simpler for JSon/Object mapping diff --git a/src/main/java/org/springframework/samples/petclinic/controller/VisitController.java b/src/main/java/org/springframework/samples/petclinic/controller/VisitController.java index 3fb9bfb4c..25c445352 100644 --- a/src/main/java/org/springframework/samples/petclinic/controller/VisitController.java +++ b/src/main/java/org/springframework/samples/petclinic/controller/VisitController.java @@ -26,6 +26,8 @@ import org.springframework.samples.petclinic.dto.PetDTO; import org.springframework.samples.petclinic.dto.VisitDTO; import org.springframework.samples.petclinic.service.PetService; import org.springframework.samples.petclinic.service.VisitService; +import org.springframework.samples.petclinic.validator.PetDTOValidator; +import org.springframework.samples.petclinic.validator.VisitDTOValidator; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.web.bind.WebDataBinder; @@ -77,6 +79,11 @@ class VisitController { return visit; } + @InitBinder("visit") + public void initVisitBinder(WebDataBinder dataBinder) { + dataBinder.setValidator(new VisitDTOValidator()); + } + // Spring MVC calls method loadPetWithVisit(...) before initNewVisitForm is called @GetMapping(CommonEndPoint.VISITS_NEW) public String initNewVisitForm(@PathVariable("petId") int petId, Map model) { diff --git a/src/main/java/org/springframework/samples/petclinic/dto/VisitDTO.java b/src/main/java/org/springframework/samples/petclinic/dto/VisitDTO.java index d976185b7..9f89a6bcf 100644 --- a/src/main/java/org/springframework/samples/petclinic/dto/VisitDTO.java +++ b/src/main/java/org/springframework/samples/petclinic/dto/VisitDTO.java @@ -17,6 +17,7 @@ package org.springframework.samples.petclinic.dto; import org.springframework.format.annotation.DateTimeFormat; import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; import java.time.LocalDate; /** @@ -26,12 +27,14 @@ import java.time.LocalDate; */ public class VisitDTO extends BaseDTO { + @NotNull @DateTimeFormat(pattern = "yyyy-MM-dd") private LocalDate date; @NotEmpty private String description; + @NotNull private Integer petId; /** @@ -76,7 +79,8 @@ public class VisitDTO extends BaseDTO { if (!getDate().equals(visitDTO.getDate())) return false; - if (!getDescription().equals(visitDTO.getDescription())) + if (getDescription() != null ? !getDescription().equals(visitDTO.getDescription()) + : visitDTO.getDescription() != null) return false; return getPetId().equals(visitDTO.getPetId()); } diff --git a/src/main/java/org/springframework/samples/petclinic/validator/VisitDTOValidator.java b/src/main/java/org/springframework/samples/petclinic/validator/VisitDTOValidator.java new file mode 100644 index 000000000..b2ee0a069 --- /dev/null +++ b/src/main/java/org/springframework/samples/petclinic/validator/VisitDTOValidator.java @@ -0,0 +1,47 @@ +package org.springframework.samples.petclinic.validator; + +import org.springframework.samples.petclinic.common.CommonAttribute; +import org.springframework.samples.petclinic.common.CommonError; +import org.springframework.samples.petclinic.dto.VisitDTO; +import org.springframework.validation.Errors; +import org.springframework.validation.Validator; + +/** + * Validator for VisitDTO forms. + *

+ * We're not using Bean Validation annotations here because it is easier to define such + * validation rule in Java. + *

+ * + * @author Paul-Emmanuel DOS SANTOS FACAO + */ +public class VisitDTOValidator implements Validator { + + @Override + public boolean supports(Class aClass) { + return VisitDTO.class.isAssignableFrom(aClass); + } + + @Override + public void validate(Object object, Errors errors) { + VisitDTO visit = (VisitDTO) object; + + // Pet ID validation + if (visit.getPetId() == null) { + errors.rejectValue(CommonAttribute.VISIT_PET_ID, CommonError.REQUIRED_ARGS, CommonError.REQUIRED_MESSAGE); + } + + // Visit date validation + if (visit.getDate() == null) { + errors.rejectValue(CommonAttribute.VISIT_DATE, CommonError.REQUIRED_ARGS, CommonError.REQUIRED_MESSAGE); + } + + // Visit description validation + if (visit.getDescription() == null || visit.getDescription().isEmpty()) { + errors.rejectValue(CommonAttribute.VISIT_DESCRIPTION, CommonError.REQUIRED_ARGS, + CommonError.REQUIRED_MESSAGE); + } + + } + +} diff --git a/src/test/java/org/springframework/samples/petclinic/controller/PetControllerIntegrationTest.java b/src/test/java/org/springframework/samples/petclinic/controller/PetControllerIntegrationTest.java index 0691fe6fc..eddd6cb60 100644 --- a/src/test/java/org/springframework/samples/petclinic/controller/PetControllerIntegrationTest.java +++ b/src/test/java/org/springframework/samples/petclinic/controller/PetControllerIntegrationTest.java @@ -36,6 +36,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. class PetControllerIntegrationTest { private static final int TEST_OWNER_ID = 5; + private static final int TEST_PET_ID = 6; @Autowired @@ -75,7 +76,8 @@ class PetControllerIntegrationTest { final MvcResult result = mockMvc .perform(get(CommonEndPoint.OWNERS_ID + CommonEndPoint.PETS_NEW, TEST_OWNER_ID) .flashAttr(CommonAttribute.OWNER, ownerDTO)) - .andExpect(status().isOk()).andExpect(view().name(CommonView.PET_CREATE_OR_UPDATE)).andReturn(); + .andExpect(status().is2xxSuccessful()).andExpect(view().name(CommonView.PET_CREATE_OR_UPDATE)) + .andReturn(); PetDTO petFound = (PetDTO) Objects.requireNonNull(result.getModelAndView()).getModel().get(CommonAttribute.PET); @@ -104,9 +106,10 @@ class PetControllerIntegrationTest { void givenPetId_whenGetUpdatePet_thenReturnUpdateViewWithPet() throws Exception { PetDTO expected = petService.entityToDTO(petRepository.findById(TEST_PET_ID)); - final MvcResult result = mockMvc.perform(get(CommonEndPoint.OWNERS_ID + CommonEndPoint.PETS_ID_EDIT, TEST_OWNER_ID, TEST_PET_ID)) - .andExpect(status().isOk()).andExpect(model().attributeExists(CommonAttribute.PET)) - .andExpect(view().name(CommonView.PET_CREATE_OR_UPDATE)).andReturn(); + final MvcResult result = mockMvc + .perform(get(CommonEndPoint.OWNERS_ID + CommonEndPoint.PETS_ID_EDIT, TEST_OWNER_ID, TEST_PET_ID)) + .andExpect(status().isOk()).andExpect(model().attributeExists(CommonAttribute.PET)) + .andExpect(view().name(CommonView.PET_CREATE_OR_UPDATE)).andReturn(); PetDTO petFound = (PetDTO) Objects.requireNonNull(result.getModelAndView()).getModel().get(CommonAttribute.PET); @@ -122,13 +125,14 @@ class PetControllerIntegrationTest { petExpected.setName("Nabucho"); petExpected.setBirthDate(LocalDate.now()); - final MvcResult result = mockMvc.perform(post(CommonEndPoint.OWNERS_ID + CommonEndPoint.PETS_ID_EDIT, ownerExpected.getId(), petExpected.getId()) - .flashAttr(CommonAttribute.OWNER, ownerExpected).flashAttr(CommonAttribute.PET, petExpected)) - .andExpect(view().name(CommonView.OWNER_OWNERS_ID_R)) - .andReturn(); + final MvcResult result = mockMvc.perform( + post(CommonEndPoint.OWNERS_ID + CommonEndPoint.PETS_ID_EDIT, ownerExpected.getId(), petExpected.getId()) + .flashAttr(CommonAttribute.OWNER, ownerExpected).flashAttr(CommonAttribute.PET, petExpected)) + .andExpect(view().name(CommonView.OWNER_OWNERS_ID_R)).andReturn(); PetDTO petFound = petService.entityToDTO(petRepository.findById(TEST_PET_ID)); assertThat(petFound).isEqualTo(petExpected); } + } diff --git a/src/test/java/org/springframework/samples/petclinic/controller/PetControllerTest.java b/src/test/java/org/springframework/samples/petclinic/controller/PetControllerTest.java index 56e00a025..fc4ed3201 100644 --- a/src/test/java/org/springframework/samples/petclinic/controller/PetControllerTest.java +++ b/src/test/java/org/springframework/samples/petclinic/controller/PetControllerTest.java @@ -57,6 +57,7 @@ import org.springframework.test.web.servlet.MockMvc; class PetControllerTest { private static final int TEST_OWNER_ID = 1; + private static final int TEST_PET_ID = 1; @Autowired @@ -96,11 +97,9 @@ class PetControllerTest { @DisplayName("Verify that call the right view with parameters when attempt to create Pet") void givenNewPet_whenPostNewPet_thenSavePetAndRedirectToOwnerView() throws Exception { mockMvc.perform(post(CommonEndPoint.OWNERS_ID + CommonEndPoint.PETS_NEW, TEST_OWNER_ID) - .param(CommonAttribute.PET_NAME, "Betty") - .param(CommonAttribute.PET_TYPE, "hamster") - .param(CommonAttribute.PET_BIRTH_DATE, "2015-02-12")) - .andExpect(status().is3xxRedirection()) - .andExpect(view().name(CommonView.OWNER_OWNERS_ID_R)); + .param(CommonAttribute.PET_NAME, "Betty").param(CommonAttribute.PET_TYPE, "hamster") + .param(CommonAttribute.PET_BIRTH_DATE, "2015-02-12")).andExpect(status().is3xxRedirection()) + .andExpect(view().name(CommonView.OWNER_OWNERS_ID_R)); } @Test @@ -108,14 +107,13 @@ class PetControllerTest { @DisplayName("Verify that return to Pet creation form when pet has no name") void givenNewPetWithoutName_whenPostNewPet_thenSavePetAndRedirectToOwnerView() throws Exception { mockMvc.perform(post(CommonEndPoint.OWNERS_ID + CommonEndPoint.PETS_NEW, TEST_OWNER_ID) - .param(CommonAttribute.PET_TYPE, "hamster") - .param(CommonAttribute.PET_BIRTH_DATE, "2015-02-12")) - .andExpect(model().attributeHasNoErrors(CommonAttribute.OWNER)) - .andExpect(model().attributeHasErrors(CommonAttribute.PET)) - .andExpect(model().attributeHasFieldErrors(CommonAttribute.PET, CommonAttribute.PET_NAME)) - .andExpect(model().attributeHasFieldErrorCode(CommonAttribute.PET, CommonAttribute.PET_NAME, CommonError.REQUIRED_ARGS)) - .andExpect(status().isOk()) - .andExpect(view().name(CommonView.PET_CREATE_OR_UPDATE)); + .param(CommonAttribute.PET_TYPE, "hamster").param(CommonAttribute.PET_BIRTH_DATE, "2015-02-12")) + .andExpect(model().attributeHasNoErrors(CommonAttribute.OWNER)) + .andExpect(model().attributeHasErrors(CommonAttribute.PET)) + .andExpect(model().attributeHasFieldErrors(CommonAttribute.PET, CommonAttribute.PET_NAME)) + .andExpect(model().attributeHasFieldErrorCode(CommonAttribute.PET, CommonAttribute.PET_NAME, + CommonError.REQUIRED_ARGS)) + .andExpect(status().isOk()).andExpect(view().name(CommonView.PET_CREATE_OR_UPDATE)); } @Test @@ -123,28 +121,26 @@ class PetControllerTest { @DisplayName("Verify that return to Pet creation form when pet has no type") void givenNewPetWithoutType_whenPostNewPet_thenSavePetAndRedirectToOwnerView() throws Exception { mockMvc.perform(post(CommonEndPoint.OWNERS_ID + CommonEndPoint.PETS_NEW, TEST_OWNER_ID) - .param(CommonAttribute.PET_NAME, "Betty") - .param(CommonAttribute.PET_BIRTH_DATE, "2015-02-12")) - .andExpect(model().attributeHasNoErrors(CommonAttribute.OWNER)) - .andExpect(model().attributeHasErrors(CommonAttribute.PET)) - .andExpect(model().attributeHasFieldErrors(CommonAttribute.PET, CommonAttribute.PET_TYPE)) - .andExpect(model().attributeHasFieldErrorCode(CommonAttribute.PET, CommonAttribute.PET_TYPE, CommonError.REQUIRED_ARGS)) - .andExpect(status().isOk()) - .andExpect(view().name(CommonView.PET_CREATE_OR_UPDATE)); + .param(CommonAttribute.PET_NAME, "Betty").param(CommonAttribute.PET_BIRTH_DATE, "2015-02-12")) + .andExpect(model().attributeHasNoErrors(CommonAttribute.OWNER)) + .andExpect(model().attributeHasErrors(CommonAttribute.PET)) + .andExpect(model().attributeHasFieldErrors(CommonAttribute.PET, CommonAttribute.PET_TYPE)) + .andExpect(model().attributeHasFieldErrorCode(CommonAttribute.PET, CommonAttribute.PET_TYPE, + CommonError.REQUIRED_ARGS)) + .andExpect(status().isOk()).andExpect(view().name(CommonView.PET_CREATE_OR_UPDATE)); } @Test @Tag("processCreationForm") @DisplayName("Verify that return to Pet creation form when pet has wrong Owner ID") void givenNewPetWithWrongOwner_whenPostNewPet_thenSavePetAndRedirectToOwnerView() throws Exception { - mockMvc.perform(post(CommonEndPoint.OWNERS_ID + CommonEndPoint.PETS_NEW, 22) - .param(CommonAttribute.PET_NAME, "Betty") - .param(CommonAttribute.PET_TYPE, "hamster") - .param(CommonAttribute.PET_BIRTH_DATE, "2015-02-12")) - .andExpect(model().attributeHasErrors(CommonAttribute.PET)) - .andExpect(model().attributeHasFieldErrorCode(CommonAttribute.PET, CommonAttribute.OWNER, CommonError.NOT_FOUND_ARGS)) - .andExpect(status().isOk()) - .andExpect(view().name(CommonView.PET_CREATE_OR_UPDATE)); + mockMvc.perform( + post(CommonEndPoint.OWNERS_ID + CommonEndPoint.PETS_NEW, 22).param(CommonAttribute.PET_NAME, "Betty") + .param(CommonAttribute.PET_TYPE, "hamster").param(CommonAttribute.PET_BIRTH_DATE, "2015-02-12")) + .andExpect(model().attributeHasErrors(CommonAttribute.PET)) + .andExpect(model().attributeHasFieldErrorCode(CommonAttribute.PET, CommonAttribute.OWNER, + CommonError.NOT_FOUND_ARGS)) + .andExpect(status().isOk()).andExpect(view().name(CommonView.PET_CREATE_OR_UPDATE)); } @Test @@ -153,14 +149,13 @@ class PetControllerTest { void givenNewPetWithoutBirthDate_whenPostNewPet_thenSavePetAndRedirectToOwnerView() throws Exception { mockMvc.perform(post(CommonEndPoint.OWNERS_ID + CommonEndPoint.PETS_NEW, TEST_OWNER_ID) - .param(CommonAttribute.PET_NAME, "Betty") - .param(CommonAttribute.PET_TYPE, "hamster")) - .andExpect(model().attributeHasNoErrors(CommonAttribute.OWNER)) - .andExpect(model().attributeHasErrors(CommonAttribute.PET)) - .andExpect(model().attributeHasFieldErrors(CommonAttribute.PET, CommonAttribute.PET_BIRTH_DATE)) - .andExpect(model().attributeHasFieldErrorCode(CommonAttribute.PET, CommonAttribute.PET_BIRTH_DATE, CommonError.REQUIRED_ARGS)) - .andExpect(status().isOk()) - .andExpect(view().name(CommonView.PET_CREATE_OR_UPDATE)); + .param(CommonAttribute.PET_NAME, "Betty").param(CommonAttribute.PET_TYPE, "hamster")) + .andExpect(model().attributeHasNoErrors(CommonAttribute.OWNER)) + .andExpect(model().attributeHasErrors(CommonAttribute.PET)) + .andExpect(model().attributeHasFieldErrors(CommonAttribute.PET, CommonAttribute.PET_BIRTH_DATE)) + .andExpect(model().attributeHasFieldErrorCode(CommonAttribute.PET, CommonAttribute.PET_BIRTH_DATE, + CommonError.REQUIRED_ARGS)) + .andExpect(status().isOk()).andExpect(view().name(CommonView.PET_CREATE_OR_UPDATE)); } @Test diff --git a/src/test/java/org/springframework/samples/petclinic/controller/VetControllerIntegrationTest.java b/src/test/java/org/springframework/samples/petclinic/controller/VetControllerIntegrationTest.java index 2cdca1f48..1ba41c95c 100644 --- a/src/test/java/org/springframework/samples/petclinic/controller/VetControllerIntegrationTest.java +++ b/src/test/java/org/springframework/samples/petclinic/controller/VetControllerIntegrationTest.java @@ -1,6 +1,5 @@ package org.springframework.samples.petclinic.controller; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; @@ -24,18 +23,21 @@ import org.springframework.test.web.servlet.MvcResult; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; +import java.util.Objects; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; /** + * Test class for the {@link VetController} + * * @author Paul-Emmanuel DOS SANTOS FACAO */ @Slf4j @SpringBootTest @AutoConfigureMockMvc -public class VetControllerIntegrationTest { +class VetControllerIntegrationTest { @Autowired private MockMvc mockMvc; @@ -54,19 +56,16 @@ public class VetControllerIntegrationTest { expected = new VetsDTO(vetService.entitiesToDTOS(new ArrayList<>(vets))); } - @Test @Tag("showVetList") @DisplayName("When asking vets get String containing Vets") void whenGetVets_thenReturnStringOfVets() throws Exception { - final MvcResult result = mockMvc.perform(get(CommonEndPoint.VETS_HTML)) - .andExpect(status().is2xxSuccessful()) - .andExpect(model().attributeExists(CommonAttribute.VETS)) - .andExpect(view().name(CommonView.VET_VETS_LIST)) - .andReturn(); + final MvcResult result = mockMvc.perform(get(CommonEndPoint.VETS_HTML)).andExpect(status().is2xxSuccessful()) + .andExpect(model().attributeExists(CommonAttribute.VETS)) + .andExpect(view().name(CommonView.VET_VETS_LIST)).andReturn(); - VetsDTO found = (VetsDTO) result.getModelAndView().getModel().get(CommonAttribute.VETS); + VetsDTO found = (VetsDTO) Objects.requireNonNull(result.getModelAndView()).getModel().get(CommonAttribute.VETS); assertThat(found).isEqualToComparingFieldByField(expected); } @@ -78,9 +77,8 @@ public class VetControllerIntegrationTest { ObjectMapper mapper = new ObjectMapper(); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - final MvcResult result = mockMvc.perform(get(CommonEndPoint.VETS)) - .andExpect(status().is2xxSuccessful()) - .andReturn(); + final MvcResult result = mockMvc.perform(get(CommonEndPoint.VETS)).andExpect(status().is2xxSuccessful()) + .andReturn(); String json = result.getResponse().getContentAsString(StandardCharsets.UTF_8); diff --git a/src/test/java/org/springframework/samples/petclinic/controller/VetControllerTest.java b/src/test/java/org/springframework/samples/petclinic/controller/VetControllerTest.java index 74ae5b20a..f72e54c67 100644 --- a/src/test/java/org/springframework/samples/petclinic/controller/VetControllerTest.java +++ b/src/test/java/org/springframework/samples/petclinic/controller/VetControllerTest.java @@ -16,6 +16,7 @@ package org.springframework.samples.petclinic.controller; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; @@ -24,11 +25,10 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; import org.assertj.core.util.Lists; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; @@ -38,11 +38,16 @@ import org.springframework.samples.petclinic.common.CommonEndPoint; import org.springframework.samples.petclinic.common.CommonView; import org.springframework.samples.petclinic.dto.SpecialtyDTO; import org.springframework.samples.petclinic.dto.VetDTO; +import org.springframework.samples.petclinic.dto.VetsDTO; import org.springframework.samples.petclinic.service.VetService; -import org.springframework.samples.petclinic.model.Specialty; import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.ResultActions; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Objects; + /** * Test class for the {@link VetController} * @@ -57,8 +62,10 @@ class VetControllerTest { @MockBean private VetService vetService; - @BeforeEach - void beforeEach() { + static List vetDTOS; + + @BeforeAll + static void beforeAll() { VetDTO james = new VetDTO(); james.setFirstName("James"); james.setLastName("Carter"); @@ -71,27 +78,43 @@ class VetControllerTest { radiology.setId(1); radiology.setName("radiology"); helen.addSpecialty(radiology); - given(this.vetService.findAll()).willReturn(Lists.newArrayList(james, helen)); + + vetDTOS = Lists.newArrayList(james, helen); + } + + @BeforeEach + void beforeEach() { + given(this.vetService.findAll()).willReturn(vetDTOS); } @Test @Tag("showVetList") @DisplayName("When asking vets get String containing Vets") void whenGetVets_thenReturnStringOfVets() throws Exception { - mockMvc.perform(get(CommonEndPoint.VETS_HTML)).andExpect(status().isOk()) + final MvcResult result = mockMvc.perform(get(CommonEndPoint.VETS_HTML)).andExpect(status().is2xxSuccessful()) .andExpect(model().attributeExists(CommonAttribute.VETS)) - .andExpect(view().name(CommonView.VET_VETS_LIST)); + .andExpect(view().name(CommonView.VET_VETS_LIST)).andReturn(); + + VetsDTO found = (VetsDTO) Objects.requireNonNull(result.getModelAndView()).getModel().get(CommonAttribute.VETS); + + assertThat(found.getVetList()).isEqualTo(vetDTOS); } @Test @Tag("showResourcesVetList") @DisplayName("When asking vets get Vets DTO object containing Vets") void whenGetVets_thenReturnVetsDTO() throws Exception { - ResultActions actions = mockMvc.perform(get(CommonEndPoint.VETS).accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()); + ObjectMapper mapper = new ObjectMapper(); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - actions.andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(jsonPath("$.vetList[0].id").value(1)); + final MvcResult result = mockMvc.perform(get(CommonEndPoint.VETS).accept(MediaType.APPLICATION_JSON)) + .andExpect(status().is2xxSuccessful()).andReturn(); + + String json = result.getResponse().getContentAsString(StandardCharsets.UTF_8); + + VetsDTO found = mapper.readValue(json, VetsDTO.class); + + assertThat(found.getVetList()).isEqualTo(vetDTOS); } } diff --git a/src/test/java/org/springframework/samples/petclinic/controller/VisitControllerIntegrationTest.java b/src/test/java/org/springframework/samples/petclinic/controller/VisitControllerIntegrationTest.java new file mode 100644 index 000000000..c35e8d3d4 --- /dev/null +++ b/src/test/java/org/springframework/samples/petclinic/controller/VisitControllerIntegrationTest.java @@ -0,0 +1,106 @@ +package org.springframework.samples.petclinic.controller; + +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.samples.petclinic.common.CommonAttribute; +import org.springframework.samples.petclinic.common.CommonEndPoint; +import org.springframework.samples.petclinic.common.CommonView; +import org.springframework.samples.petclinic.dto.PetDTO; +import org.springframework.samples.petclinic.dto.VisitDTO; +import org.springframework.samples.petclinic.model.Visit; +import org.springframework.samples.petclinic.repository.VisitRepository; +import org.springframework.samples.petclinic.service.PetService; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; + +import java.time.LocalDate; +import java.util.List; +import java.util.Objects; + +import static org.assertj.core.api.Assertions.assertThat; +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.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; + +/** + * @author Paul-Emmanuel DOS SANTOS FACAO + */ +@Slf4j +@SpringBootTest +@AutoConfigureMockMvc +class VisitControllerIntegrationTest { + + // An existing Pet ID in database + private final static Integer PET_ID = 5; + + @Autowired + private MockMvc mockMvc; + + @Autowired + private VisitRepository visitRepository; + + @Autowired + private PetService petService; + + private PetDTO petDTO; + + private Visit visit; + + private VisitDTO visitDTO; + + @BeforeEach + void beforeEach() { + petDTO = petService.findById(PET_ID); + + visit = new Visit(); + visit.setPetId(PET_ID); + visit.setDate(LocalDate.now()); + visit.setDescription("Visit Description"); + + visitDTO = new VisitDTO(); + visitDTO.setPetId(PET_ID); + visitDTO.setDate(LocalDate.now()); + visitDTO.setDescription("Visit Description"); + } + + @Test + @Tag("initNewVisitForm") + @DisplayName("Verify that return form for new Visit with right Pet") + void givenPetId_whenGetNewVisit_thenReturnCreationViewWithNewVisit() throws Exception { + Integer nbOfVisits = petDTO.getVisits().size(); + + final MvcResult result = mockMvc.perform(get(CommonEndPoint.VISITS_NEW, PET_ID)) + .andExpect(status().is2xxSuccessful()).andExpect(view().name(CommonView.VISIT_CREATE_OR_UPDATE)) + .andReturn(); + + PetDTO found = (PetDTO) Objects.requireNonNull(result.getModelAndView()).getModel().get(CommonAttribute.PET); + + assertThat(found).isEqualToIgnoringGivenFields(petDTO, CommonAttribute.VISITS); + assertThat(found.getVisits().size()).isEqualTo(nbOfVisits + 1); + + VisitDTO expected = new VisitDTO(); + expected.setDate(LocalDate.now()); + expected.setPetId(PET_ID); + + assertThat(found.getVisits()).contains(expected); + } + + @Test + @Tag("processNewVisitForm") + @DisplayName("Verify that save Visit") + void givenVisitAndOwnerIDAndPetId_whenSaveVisit_thenSaveVisit() throws Exception { + + mockMvc.perform(post(CommonEndPoint.VISITS_EDIT, petDTO.getOwner().getId(), PET_ID) + .flashAttr(CommonAttribute.VISIT, visitDTO)).andExpect(status().is3xxRedirection()) + .andExpect(view().name(CommonView.OWNER_OWNERS_ID_R)).andReturn(); + + List found = visitRepository.findByPetId(PET_ID); + + assertThat(found).contains(visit); + } + +} diff --git a/src/test/java/org/springframework/samples/petclinic/controller/VisitControllerTest.java b/src/test/java/org/springframework/samples/petclinic/controller/VisitControllerTest.java index cd7d13927..9135164d4 100644 --- a/src/test/java/org/springframework/samples/petclinic/controller/VisitControllerTest.java +++ b/src/test/java/org/springframework/samples/petclinic/controller/VisitControllerTest.java @@ -16,16 +16,19 @@ package org.springframework.samples.petclinic.controller; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; 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.result.MockMvcResultMatchers.model; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; @@ -33,19 +36,40 @@ import org.springframework.samples.petclinic.common.CommonAttribute; import org.springframework.samples.petclinic.common.CommonEndPoint; import org.springframework.samples.petclinic.common.CommonView; import org.springframework.samples.petclinic.dto.PetDTO; +import org.springframework.samples.petclinic.dto.PetTypeDTO; +import org.springframework.samples.petclinic.dto.VisitDTO; +import org.springframework.samples.petclinic.model.Visit; import org.springframework.samples.petclinic.service.PetService; import org.springframework.samples.petclinic.service.VisitService; import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; + +import java.time.LocalDate; +import java.util.Objects; /** * Test class for {@link VisitController} * * @author Colin But + * @author Paul-Emmanuel DOS SANTOS FACAO */ @WebMvcTest(VisitController.class) +@ExtendWith(MockitoExtension.class) class VisitControllerTest { - private static final int TEST_PET_ID = 1; + private final static Integer OWNER_ID = 5; + + private final static Integer PET_ID = 14; + + private final static Integer PET_TYPE_ID = 6; + + private final static String PET_TYPE_NAME = "dragon"; + + private final static String PET_NAME = "bowser"; + + private final static String PET_BIRTH_DATE = "2020-07-11"; + + private final static String VISIT_DESCRIPTION = "Visit Description"; @Autowired private MockMvc mockMvc; @@ -56,31 +80,88 @@ class VisitControllerTest { @MockBean private PetService petService; + static private PetDTO petDTO; + + private Visit visit; + + private VisitDTO visitDTO; + + @BeforeAll + static void beforeAll() { + PetTypeDTO petTypeDTO = new PetTypeDTO(); + petTypeDTO.setName(PET_TYPE_NAME); + petTypeDTO.setId(PET_TYPE_ID); + petDTO = new PetDTO(); + petDTO.setId(PET_ID); + petDTO.setName(PET_NAME); + petDTO.setType(petTypeDTO); + petDTO.setBirthDate(LocalDate.parse(PET_BIRTH_DATE)); + } + @BeforeEach - void init() { - given(this.petService.findById(TEST_PET_ID)).willReturn(new PetDTO()); + void beforeEach() { + visitDTO = new VisitDTO(); + visitDTO.setPetId(PET_ID); + visitDTO.setDate(LocalDate.now()); + visitDTO.setDescription(VISIT_DESCRIPTION); } @Test @Tag("initNewVisitForm") + @DisplayName("Verify that return form for new Visit with right Pet") void testInitNewVisitForm() throws Exception { - mockMvc.perform(get(CommonEndPoint.VISITS_NEW, TEST_PET_ID)).andExpect(status().isOk()) + given(this.petService.findById(PET_ID)).willReturn(petDTO); + + final MvcResult result = mockMvc.perform(get(CommonEndPoint.VISITS_NEW, PET_ID)) + .andExpect(status().is2xxSuccessful()).andExpect(view().name(CommonView.VISIT_CREATE_OR_UPDATE)) + .andReturn(); + + PetDTO found = (PetDTO) Objects.requireNonNull(result.getModelAndView()).getModel().get(CommonAttribute.PET); + + assertThat(found).isEqualToIgnoringGivenFields(petDTO, CommonAttribute.VISITS); + + VisitDTO expected = new VisitDTO(); + expected.setDate(LocalDate.now()); + expected.setPetId(PET_ID); + + assertThat(found.getVisits()).contains(expected); + } + + @Test + @Tag("processNewVisitForm") + @DisplayName("Verify that save Visit") + void givenVisitAndOwnerIDAndPetId_whenSaveVisit_thenSaveVisit() throws Exception { + ArgumentCaptor captor = ArgumentCaptor.forClass(VisitDTO.class); + + mockMvc.perform(post(CommonEndPoint.VISITS_EDIT, OWNER_ID, PET_ID).flashAttr(CommonAttribute.VISIT, visitDTO)) + .andExpect(status().is3xxRedirection()).andExpect(view().name(CommonView.OWNER_OWNERS_ID_R)) + .andReturn(); + + verify(visitService, times(1)).save(captor.capture()); + + assertThat(captor.getValue()).isEqualTo(visitDTO); + } + + @Test + @Tag("processNewVisitForm") + @DisplayName("Verify that return to update Visit view if Visit has no date") + void givenVisitAndOwnerIDAndPetIdAndVisitWithoutDate_whenSaveVisit_thenReturnToCreationView() throws Exception { + visitDTO.setDate(null); + + mockMvc.perform(post(CommonEndPoint.VISITS_EDIT, OWNER_ID, PET_ID).flashAttr(CommonAttribute.PET, petDTO) + .flashAttr(CommonAttribute.VISIT, visitDTO)).andExpect(status().is2xxSuccessful()) .andExpect(view().name(CommonView.VISIT_CREATE_OR_UPDATE)); } @Test @Tag("processNewVisitForm") - void testProcessNewVisitFormSuccess() throws Exception { - mockMvc.perform(post(CommonEndPoint.VISITS_NEW, TEST_PET_ID).param(CommonAttribute.NAME, "George") - .param(CommonAttribute.DESCRIPTION, "Visit Description")).andExpect(status().is3xxRedirection()) - .andExpect(view().name(CommonView.OWNER_OWNERS_ID_R)); - } + @DisplayName("Verify that return to update Visit view if Visit has no description") + void givenVisitAndOwnerIDAndPetIdAndVisitWithoutDescription_whenSaveVisit_thenReturnToCreationView() + throws Exception { + visitDTO.setDescription(""); - @Test - @Tag("processNewVisitForm") - void testProcessNewVisitFormHasErrors() throws Exception { - mockMvc.perform(post(CommonEndPoint.VISITS_NEW, TEST_PET_ID).param(CommonAttribute.NAME, "George")) - .andExpect(model().attributeHasErrors(CommonAttribute.VISIT)).andExpect(status().isOk()) + mockMvc.perform(post(CommonEndPoint.VISITS_EDIT, OWNER_ID, PET_ID).flashAttr(CommonAttribute.PET, petDTO) + .flashAttr(CommonAttribute.VISIT, visitDTO)).andExpect(status().is2xxSuccessful()) .andExpect(view().name(CommonView.VISIT_CREATE_OR_UPDATE)); }