Add pagination for owners and vets lists in HTML

Fixes #91
This commit is contained in:
simrin051 2021-09-13 20:11:55 +05:30 committed by Dave Syer
parent 25ba1621a9
commit 8ad9c05f74
27 changed files with 323 additions and 192 deletions

View file

@ -15,12 +15,11 @@
*/
package org.springframework.samples.petclinic.model;
import java.io.Serializable;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import java.io.Serializable;
/**
* Simple JavaBean domain object with an id property. Used as a base class for objects

View file

@ -24,6 +24,7 @@ import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.validation.constraints.Digits;
@ -59,7 +60,7 @@ public class Owner extends Person {
@Digits(fraction = 0, integer = 10)
private String telephone;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "owner")
@OneToMany(cascade = CascadeType.ALL, mappedBy = "owner", fetch = FetchType.EAGER)
private Set<Pet> pets;
public String getAddress() {

View file

@ -15,6 +15,14 @@
*/
package org.springframework.samples.petclinic.owner;
import java.util.List;
import java.util.Map;
import javax.validation.Valid;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.samples.petclinic.visit.VisitRepository;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
@ -24,12 +32,9 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import javax.validation.Valid;
import java.util.Collection;
import java.util.Map;
/**
* @author Juergen Hoeller
* @author Ken Krebs
@ -43,7 +48,7 @@ class OwnerController {
private final OwnerRepository owners;
private VisitRepository visits;
private final VisitRepository visits;
public OwnerController(OwnerRepository clinicService, VisitRepository visits) {
this.owners = clinicService;
@ -80,7 +85,8 @@ class OwnerController {
}
@GetMapping("/owners")
public String processFindForm(Owner owner, BindingResult result, Map<String, Object> model) {
public String processFindForm(@RequestParam(defaultValue = "1") int page, Owner owner, BindingResult result,
Model model) {
// allow parameterless GET request for /owners to return all records
if (owner.getLastName() == null) {
@ -88,24 +94,43 @@ class OwnerController {
}
// find owners by last name
Collection<Owner> results = this.owners.findByLastName(owner.getLastName());
if (results.isEmpty()) {
String lastName = owner.getLastName();
Page<Owner> ownersResults = findPaginatedForOwnersLastName(page, lastName);
if (ownersResults.isEmpty()) {
// no owners found
result.rejectValue("lastName", "notFound", "not found");
return "owners/findOwners";
}
else if (results.size() == 1) {
else if (ownersResults.getTotalElements() == 1) {
// 1 owner found
owner = results.iterator().next();
owner = ownersResults.iterator().next();
return "redirect:/owners/" + owner.getId();
}
else {
// multiple owners found
model.put("selections", results);
return "owners/ownersList";
lastName = owner.getLastName();
return addPaginationModel(page, model, lastName, ownersResults);
}
}
private String addPaginationModel(int page, Model model, String lastName, Page<Owner> paginated) {
model.addAttribute("listOwners", paginated);
List<Owner> listOwners = paginated.getContent();
model.addAttribute("currentPage", page);
model.addAttribute("totalPages", paginated.getTotalPages());
model.addAttribute("totalItems", paginated.getTotalElements());
model.addAttribute("listOwners", listOwners);
return "owners/ownersList";
}
private Page<Owner> findPaginatedForOwnersLastName(int page, String lastname) {
int pageSize = 5;
Pageable pageable = PageRequest.of(page - 1, pageSize);
return owners.findByLastName(lastname, pageable);
}
@GetMapping("/owners/{ownerId}/edit")
public String initUpdateOwnerForm(@PathVariable("ownerId") int ownerId, Model model) {
Owner owner = this.owners.findById(ownerId);

View file

@ -15,8 +15,8 @@
*/
package org.springframework.samples.petclinic.owner;
import java.util.Collection;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.query.Param;
@ -42,9 +42,10 @@ public interface OwnerRepository extends Repository<Owner, Integer> {
* @return a Collection of matching {@link Owner}s (or an empty Collection if none
* found)
*/
@Query("SELECT DISTINCT owner FROM Owner owner left join fetch owner.pets WHERE owner.lastName LIKE :lastName%")
@Query("SELECT DISTINCT owner FROM Owner owner left join owner.pets WHERE owner.lastName LIKE :lastName% ")
@Transactional(readOnly = true)
Collection<Owner> findByLastName(@Param("lastName") String lastName);
Page<Owner> findByLastName(@Param("lastName") String lastName, Pageable pageable);
/**
* Retrieve an {@link Owner} from the data store by id.
@ -61,4 +62,11 @@ public interface OwnerRepository extends Repository<Owner, Integer> {
*/
void save(Owner owner);
/**
* Returnes all the owners from data store
**/
@Query("SELECT owner FROM Owner owner")
@Transactional(readOnly = true)
Page<Owner> findAll(Pageable pageable);
}

View file

@ -15,12 +15,12 @@
*/
package org.springframework.samples.petclinic.owner;
import java.util.List;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* Repository class for <code>Pet</code> domain objects All method names are compliant
* with Spring Data naming conventions so this interface can easily be extended for Spring

View file

@ -15,11 +15,11 @@
*/
package org.springframework.samples.petclinic.owner;
import org.springframework.samples.petclinic.model.NamedEntity;
import javax.persistence.Entity;
import javax.persistence.Table;
import org.springframework.samples.petclinic.model.NamedEntity;
/**
* @author Juergen Hoeller Can be Cat, Dog, Hamster...
*/

View file

@ -15,14 +15,14 @@
*/
package org.springframework.samples.petclinic.owner;
import java.text.ParseException;
import java.util.Collection;
import java.util.Locale;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.Formatter;
import org.springframework.stereotype.Component;
import java.text.ParseException;
import java.util.Collection;
import java.util.Locale;
/**
* Instructs Spring MVC on how to parse and print elements of type 'PetType'. Starting
* from Spring 3.0, Formatters have come as an improvement in comparison to legacy

View file

@ -15,20 +15,15 @@
*/
package org.springframework.samples.petclinic.owner;
import java.util.Map;
import javax.validation.Valid;
import org.springframework.samples.petclinic.visit.Visit;
import org.springframework.samples.petclinic.visit.VisitRepository;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.Map;
/**
* @author Juergen Hoeller

View file

@ -16,13 +16,13 @@
package org.springframework.samples.petclinic.system;
import javax.cache.configuration.MutableConfiguration;
import org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.cache.configuration.MutableConfiguration;
/**
* Cache configuration intended for caches providing the JCache API. This configuration
* creates the used cache for the application and enables statistics that become

View file

@ -15,12 +15,11 @@
*/
package org.springframework.samples.petclinic.vet;
import java.io.Serializable;
import org.springframework.samples.petclinic.model.NamedEntity;
import javax.persistence.Entity;
import javax.persistence.Table;
import org.springframework.samples.petclinic.model.NamedEntity;
import java.io.Serializable;
/**
* Models a {@link Vet Vet's} specialty (for example, dentistry).

View file

@ -15,24 +15,14 @@
*/
package org.springframework.samples.petclinic.vet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlElement;
import org.springframework.beans.support.MutableSortDefinition;
import org.springframework.beans.support.PropertyComparator;
import org.springframework.samples.petclinic.model.Person;
import javax.persistence.*;
import javax.xml.bind.annotation.XmlElement;
import java.util.*;
/**
* Simple JavaBean domain object representing a veterinarian.
*

View file

@ -15,11 +15,17 @@
*/
package org.springframework.samples.petclinic.vet;
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;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Map;
import java.util.List;
/**
* @author Juergen Hoeller
@ -37,15 +43,31 @@ class VetController {
}
@GetMapping("/vets.html")
public String showVetList(Map<String, Object> model) {
public String showVetList(@RequestParam(defaultValue = "1") int page, Model model) {
// Here we are returning an object of type 'Vets' rather than a collection of Vet
// objects so it is simpler for Object-Xml mapping
Vets vets = new Vets();
vets.getVetList().addAll(this.vets.findAll());
model.put("vets", vets);
Page<Vet> paginated = findPaginated(page);
vets.getVetList().addAll(paginated.toList());
return addPaginationModel(page, paginated, model);
}
private String addPaginationModel(int page, Page<Vet> paginated, Model model) {
List<Vet> listVets = paginated.getContent();
model.addAttribute("currentPage", page);
model.addAttribute("totalPages", paginated.getTotalPages());
model.addAttribute("totalItems", paginated.getTotalElements());
model.addAttribute("listVets", listVets);
return "vets/vetList";
}
private Page<Vet> findPaginated(int page) {
int pageSize = 5;
Pageable pageable = PageRequest.of(page - 1, pageSize);
return vets.findAll(pageable);
}
@GetMapping({ "/vets" })
public @ResponseBody Vets showResourcesVetList() {
// Here we are returning an object of type 'Vets' rather than a collection of Vet

View file

@ -15,13 +15,15 @@
*/
package org.springframework.samples.petclinic.vet;
import java.util.Collection;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.dao.DataAccessException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
/**
* Repository class for <code>Vet</code> domain objects All method names are compliant
* with Spring Data naming conventions so this interface can easily be extended for Spring
@ -43,4 +45,16 @@ public interface VetRepository extends Repository<Vet, Integer> {
@Cacheable("vets")
Collection<Vet> findAll() throws DataAccessException;
/**
* Retrieve all <code>Vet</code>s from data store in Pages
* @param pageable
* @return
* @throws DataAccessException
*/
@Transactional(readOnly = true)
@Cacheable("vets")
Page<Vet> findAll(Pageable pageable) throws DataAccessException;
;
}

View file

@ -15,11 +15,10 @@
*/
package org.springframework.samples.petclinic.vet;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.ArrayList;
import java.util.List;
/**
* Simple domain object representing a list of veterinarians. Mostly here to be used for

View file

@ -15,15 +15,14 @@
*/
package org.springframework.samples.petclinic.visit;
import java.time.LocalDate;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.samples.petclinic.model.BaseEntity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.validation.constraints.NotEmpty;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.samples.petclinic.model.BaseEntity;
import java.time.LocalDate;
/**
* Simple JavaBean domain object representing a visit.

View file

@ -15,12 +15,12 @@
*/
package org.springframework.samples.petclinic.visit;
import java.util.List;
import org.springframework.dao.DataAccessException;
import org.springframework.data.repository.Repository;
import org.springframework.samples.petclinic.model.BaseEntity;
import java.util.List;
/**
* Repository class for <code>Visit</code> domain objects All method names are compliant
* with Spring Data naming conventions so this interface can easily be extended for Spring

View file

@ -2,32 +2,61 @@
<html xmlns:th="https://www.thymeleaf.org" th:replace="~{fragments/layout :: layout (~{::body},'owners')}">
<body>
<body>
<h2>Owners</h2>
<h2>Owners</h2>
<table id="owners" class="table table-striped">
<thead>
<tr>
<th style="width: 150px;">Name</th>
<th style="width: 200px;">Address</th>
<th>City</th>
<th style="width: 120px">Telephone</th>
<th>Pets</th>
</tr>
</thead>
<tbody>
<tr th:each="owner : ${selections}">
<td>
<a th:href="@{/owners/__${owner.id}__}" th:text="${owner.firstName + ' ' + owner.lastName}"/></a>
</td>
<td th:text="${owner.address}"/>
<td th:text="${owner.city}"/>
<td th:text="${owner.telephone}"/>
<td><span th:text="${#strings.listJoin(owner.pets, ', ')}"/></td>
</tr>
</tbody>
</table>
</body>
<table id="owners" class="table table-striped">
<thead>
<tr>
<th style="width: 150px;">Name</th>
<th style="width: 200px;">Address</th>
<th>City</th>
<th style="width: 120px">Telephone</th>
<th>Pets</th>
</tr>
</thead>
<tbody>
<tr th:each="owner : ${listOwners}">
<td>
<a th:href="@{/owners/__${owner.id}__}" th:text="${owner.firstName + ' ' + owner.lastName}"/></a>
</td>
<td th:text="${owner.address}"/>
<td th:text="${owner.city}"/>
<td th:text="${owner.telephone}"/>
<td><span th:text="${#strings.listJoin(owner.pets, ', ')}"/></td>
</tr>
</tbody>
</table>
<div th:if="${totalPages > 1}">
<span>Pages:</span>
<span>[</span>
<span th:each="i: ${#numbers.sequence(1, totalPages)}">
<a th:if="${currentPage != i}" th:href="@{'/owners/' + ${i}}">[[${i}]]</a>
<span th:unless="${currentPage != i}">[[${i}]]</span>
</span>
<span>]&nbsp;</span>
<span>
<a th:if="${currentPage > 1}" th:href="@{'/owners/?page=1'}" title="First"
class="glyphicon glyphicon-backward"></a>
<span th:unless="${currentPage > 1}" title="First" class="glyphicon glyphicon-backward"></span>
</span>
<span>
<a th:if="${currentPage > 1}" th:href="@{'/owners/?page=' + ${currentPage - 1}}" title="Previous"
class="glyphicon glyphicon-triangle-left"></a>
<span th:unless="${currentPage > 1}" title="Previous" class="glyphicon glyphicon-triangle-left"></span>
</span>
<span>
<a th:if="${currentPage < totalPages}" th:href="@{'/owners/?page=' + ${currentPage + 1}}" title="Next"
class="glyphicon glyphicon-triangle-right"></a>
<span th:unless="${currentPage < totalPages}" title="Next" class="glyphicon glyphicon-triangle-right"></span>
</span>
<span>
<a th:if="${currentPage < totalPages}" th:href="@{'/owners/?page=' + ${totalPages}}" title="Last"
class="glyphicon glyphicon-forward"></a>
<span th:unless="${currentPage < totalPages}" title="Last" class="glyphicon glyphicon-forward"></span>
</span>
</div>
</body>
</html>

View file

@ -1,27 +1,57 @@
<!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org"
th:replace="~{fragments/layout :: layout (~{::body},'vets')}">
th:replace="~{fragments/layout :: layout (~{::body},'vets')}">
<body>
<h2>Veterinarians</h2>
<h2>Veterinarians</h2>
<table id="vets" class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Specialties</th>
</tr>
</thead>
<tbody>
<tr th:each="vet : ${vets.vetList}">
<td th:text="${vet.firstName + ' ' + vet.lastName}"></td>
<td><span th:each="specialty : ${vet.specialties}"
th:text="${specialty.name + ' '}" /> <span
th:if="${vet.nrOfSpecialties == 0}">none</span></td>
</tr>
</tbody>
</table>
<table id="vets" class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Specialties</th>
</tr>
</thead>
<tbody>
<tr th:each="vet : ${listVets}">
<td th:text="${vet.firstName + ' ' + vet.lastName}"></td>
<td><span th:each="specialty : ${vet.specialties}"
th:text="${specialty.name + ' '}"/> <span
th:if="${vet.nrOfSpecialties == 0}">none</span></td>
</tr>
</tbody>
</table>
<div th:if="${totalPages > 1}">
<span>Pages:</span>
<span>[</span>
<span th:each="i: ${#numbers.sequence(1, totalPages)}">
<a th:if="${currentPage != i}" th:href="@{'/vets/' + ${i}}">[[${i}]]</a>
<span th:unless="${currentPage != i}">[[${i}]]</span>
</span>
<span>]&nbsp;</span>
<span>
<a th:if="${currentPage > 1}" th:href="@{'/vets.html/?page=1'}" title="First"
class="glyphicon glyphicon-backward"></a>
<span th:unless="${currentPage > 1}" title="First" class="glyphicon glyphicon-backward"></span>
</span>
<span>
<a th:if="${currentPage > 1}" th:href="@{'/vets.html/?page=' + ${currentPage - 1}}" title="Previous"
class="glyphicon glyphicon-triangle-left"></a>
<span th:unless="${currentPage > 1}" title="Previous" class="glyphicon glyphicon-triangle-left"></span>
</span>
<span>
<a th:if="${currentPage < totalPages}" th:href="@{'/vets.html/?page=' + ${currentPage + 1}}" title="Next"
class="glyphicon glyphicon-triangle-right"></a>
<span th:unless="${currentPage < totalPages}" title="Next" class="glyphicon glyphicon-triangle-right"></span>
</span>
<span>
<a th:if="${currentPage < totalPages}" th:href="@{'/vets.html/?page=' + ${totalPages}}" title="Last"
class="glyphicon glyphicon-forward"></a>
<span th:unless="${currentPage < totalPages}" title="Last" class="glyphicon glyphicon-forward"></span>
</span>
</div>
</body>
</html>

View file

@ -16,16 +16,15 @@
package org.springframework.samples.petclinic.model;
import java.util.Locale;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import org.junit.jupiter.api.Test;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import java.util.Locale;
import java.util.Set;
import static org.assertj.core.api.Assertions.assertThat;
/**

View file

@ -16,32 +16,34 @@
package org.springframework.samples.petclinic.owner;
import java.time.LocalDate;
import java.util.Collections;
import java.util.List;
import org.assertj.core.util.Lists;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
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.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.samples.petclinic.visit.Visit;
import org.springframework.samples.petclinic.visit.VisitRepository;
import org.springframework.test.web.servlet.MockMvc;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.hasProperty;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import java.time.LocalDate;
import java.util.Collections;
import java.util.List;
import static org.hamcrest.Matchers.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.BDDMockito.given;
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 static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
/**
* Test class for {@link OwnerController}
@ -81,10 +83,17 @@ class OwnerControllerTests {
max.setName("Max");
max.setBirthDate(LocalDate.now());
george.setPetsInternal(Collections.singleton(max));
given(this.owners.findByLastName(eq("Franklin"), any(Pageable.class)))
.willReturn(new PageImpl<Owner>(Lists.newArrayList(george)));
given(this.owners.findAll(any(Pageable.class))).willReturn(new PageImpl<Owner>(Lists.newArrayList(george)));
given(this.owners.findById(TEST_OWNER_ID)).willReturn(george);
Visit visit = new Visit();
visit.setDate(LocalDate.now());
given(this.visits.findByPetId(max.getId())).willReturn(Collections.singletonList(visit));
}
@Test
@ -118,23 +127,35 @@ class OwnerControllerTests {
@Test
void testProcessFindFormSuccess() throws Exception {
given(this.owners.findByLastName("")).willReturn(Lists.newArrayList(george, new Owner()));
mockMvc.perform(get("/owners")).andExpect(status().isOk()).andExpect(view().name("owners/ownersList"));
Page<Owner> tasks = Mockito.mock(Page.class);
Mockito.when(this.owners.findByLastName(anyString(), org.mockito.Matchers.isA(Pageable.class)))
.thenReturn(tasks);
mockMvc.perform(get("/owners?page=1")).andExpect(status().isOk()).andExpect(view().name("owners/ownersList"));
}
@Test
void testProcessFindFormByLastName() throws Exception {
given(this.owners.findByLastName(george.getLastName())).willReturn(Lists.newArrayList(george));
mockMvc.perform(get("/owners").param("lastName", "Franklin")).andExpect(status().is3xxRedirection())
Page<Owner> tasks = new PageImpl<Owner>(Lists.newArrayList(george));
String lastName = george.getLastName();
Mockito.when(this.owners.findByLastName(eq("Franklin"), any(Pageable.class))).thenReturn(tasks);
mockMvc.perform(get("/owners?page=1").param("lastName", "Franklin")).andExpect(status().is3xxRedirection())
.andExpect(view().name("redirect:/owners/" + TEST_OWNER_ID));
}
@Test
void testProcessFindFormNoOwnersFound() throws Exception {
mockMvc.perform(get("/owners").param("lastName", "Unknown Surname")).andExpect(status().isOk())
int pageNumber = 0;
int pageSize = 1, size = 0;
Pageable pageable = PageRequest.of(pageNumber, pageSize);
Page<Owner> tasks = new PageImpl<Owner>(Lists.newArrayList());
Mockito.when(this.owners.findByLastName(eq("Unknown Surname"), org.mockito.Matchers.isA(Pageable.class)))
.thenReturn(tasks);
mockMvc.perform(get("/owners?page=1").param("lastName", "Unknown Surname")).andExpect(status().isOk())
.andExpect(model().attributeHasFieldErrors("owner", "lastName"))
.andExpect(model().attributeHasFieldErrorCode("owner", "lastName", "notFound"))
.andExpect(view().name("owners/findOwners"));
}
@Test

View file

@ -16,13 +16,6 @@
package org.springframework.samples.petclinic.owner;
import static org.mockito.BDDMockito.given;
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.assertj.core.util.Lists;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -33,6 +26,11 @@ import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.BDDMockito.given;
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.*;
/**
* Test class for the {@link PetController}
*

View file

@ -16,12 +16,6 @@
package org.springframework.samples.petclinic.owner;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -29,6 +23,12 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;

View file

@ -16,13 +16,6 @@
package org.springframework.samples.petclinic.owner;
import static org.mockito.BDDMockito.given;
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.Test;
import org.springframework.beans.factory.annotation.Autowired;
@ -31,6 +24,11 @@ import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.samples.petclinic.visit.VisitRepository;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.BDDMockito.given;
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.*;
/**
* Test class for {@link VisitController}
*

View file

@ -16,20 +16,13 @@
package org.springframework.samples.petclinic.service;
import static org.assertj.core.api.Assertions.assertThat;
import java.time.LocalDate;
import java.util.Collection;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.samples.petclinic.owner.Owner;
import org.springframework.samples.petclinic.owner.OwnerRepository;
import org.springframework.samples.petclinic.owner.Pet;
import org.springframework.samples.petclinic.owner.PetRepository;
import org.springframework.samples.petclinic.owner.PetType;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.samples.petclinic.owner.*;
import org.springframework.samples.petclinic.vet.Vet;
import org.springframework.samples.petclinic.vet.VetRepository;
import org.springframework.samples.petclinic.visit.Visit;
@ -37,6 +30,11 @@ import org.springframework.samples.petclinic.visit.VisitRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDate;
import java.util.Collection;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration test of the Service and the Repository layer.
* <p>
@ -48,8 +46,7 @@ import org.springframework.transaction.annotation.Transactional;
* time between test execution.</li>
* <li><strong>Dependency Injection</strong> of test fixture instances, meaning that we
* don't need to perform application context lookups. See the use of
* {@link Autowired @Autowired} on the <code>{@link
* ClinicServiceTests#clinicService clinicService}</code> instance variable, which uses
* {@link Autowired @Autowired} on the <code> </code> instance variable, which uses
* autowiring <em>by type</em>.
* <li><strong>Transaction management</strong>, meaning each test method is executed in
* its own transaction, which is automatically rolled back by default. Thus, even if tests
@ -81,12 +78,14 @@ class ClinicServiceTests {
@Autowired
protected VetRepository vets;
Pageable pageable;
@Test
void shouldFindOwnersByLastName() {
Collection<Owner> owners = this.owners.findByLastName("Davis");
Page<Owner> owners = this.owners.findByLastName("Davis", pageable);
assertThat(owners).hasSize(2);
owners = this.owners.findByLastName("Daviss");
owners = this.owners.findByLastName("Daviss", pageable);
assertThat(owners).isEmpty();
}
@ -102,8 +101,8 @@ class ClinicServiceTests {
@Test
@Transactional
void shouldInsertOwner() {
Collection<Owner> owners = this.owners.findByLastName("Schultz");
int found = owners.size();
Page<Owner> owners = this.owners.findByLastName("Schultz", pageable);
int found = (int) owners.getTotalElements();
Owner owner = new Owner();
owner.setFirstName("Sam");
@ -114,8 +113,8 @@ class ClinicServiceTests {
this.owners.save(owner);
assertThat(owner.getId().longValue()).isNotEqualTo(0);
owners = this.owners.findByLastName("Schultz");
assertThat(owners.size()).isEqualTo(found + 1);
owners = this.owners.findByLastName("Schultz", pageable);
assertThat(owners.getTotalElements()).isEqualTo(found + 1);
}
@Test

View file

@ -16,11 +16,11 @@
package org.springframework.samples.petclinic.service;
import java.util.Collection;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.samples.petclinic.model.BaseEntity;
import java.util.Collection;
/**
* Utility methods for handling entities. Separate from the BaseEntity class mainly
* because of dependency on the ORM-associated ObjectRetrievalFailureException.

View file

@ -23,10 +23,7 @@ import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl;
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 static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
/**
* Test class for {@link CrashController}

View file

@ -16,27 +16,28 @@
package org.springframework.samples.petclinic.vet;
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;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
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.assertj.core.util.Lists;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
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.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
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.MockMvcRequestBuilders;
import static org.mockito.ArgumentMatchers.any;
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.*;
/**
* Test class for the {@link VetController}
*/
@WebMvcTest(VetController.class)
class VetControllerTests {
@ -46,13 +47,17 @@ class VetControllerTests {
@MockBean
private VetRepository vets;
private Vet james;
private Vet helen;
@BeforeEach
void setup() {
Vet james = new Vet();
james = new Vet();
james.setFirstName("James");
james.setLastName("Carter");
james.setId(1);
Vet helen = new Vet();
helen = new Vet();
helen.setFirstName("Helen");
helen.setLastName("Leary");
helen.setId(2);
@ -61,12 +66,16 @@ class VetControllerTests {
radiology.setName("radiology");
helen.addSpecialty(radiology);
given(this.vets.findAll()).willReturn(Lists.newArrayList(james, helen));
given(this.vets.findAll(any(Pageable.class))).willReturn(new PageImpl<Vet>(Lists.newArrayList(james, helen)));
}
@Test
void testShowVetListHtml() throws Exception {
mockMvc.perform(get("/vets.html")).andExpect(status().isOk()).andExpect(model().attributeExists("vets"))
.andExpect(view().name("vets/vetList"));
mockMvc.perform(MockMvcRequestBuilders.get("/vets.html?page=1")).andExpect(status().isOk())
.andExpect(model().attributeExists("listVets")).andExpect(view().name("vets/vetList"));
}
@Test