UTOP bot: Test improvements for OwnerControllerTests class

This commit is contained in:
UTOP Bot 2025-04-09 13:43:12 +02:00
parent 6148ddd967
commit e30d8e83ba
2 changed files with 266 additions and 263 deletions

49
pom.xml
View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version='1.0' encoding='utf-8'?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@ -6,7 +6,7 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.0</version> <version>3.4.0</version>
<relativePath></relativePath> <relativePath />
</parent> </parent>
<groupId>org.springframework.samples</groupId> <groupId>org.springframework.samples</groupId>
@ -17,15 +17,14 @@
<properties> <properties>
<!-- Generic properties -->
<java.version>17</java.version> <java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- Important for reproducible builds. Update using e.g. ./mvnw versions:set
-DnewVersion=... -->
<project.build.outputTimestamp>2024-11-28T14:37:52Z</project.build.outputTimestamp> <project.build.outputTimestamp>2024-11-28T14:37:52Z</project.build.outputTimestamp>
<!-- Web dependencies -->
<webjars-locator.version>1.0.1</webjars-locator.version> <webjars-locator.version>1.0.1</webjars-locator.version>
<webjars-bootstrap.version>5.3.3</webjars-bootstrap.version> <webjars-bootstrap.version>5.3.3</webjars-bootstrap.version>
<webjars-font-awesome.version>4.7.0</webjars-font-awesome.version> <webjars-font-awesome.version>4.7.0</webjars-font-awesome.version>
@ -41,7 +40,7 @@
</properties> </properties>
<dependencies> <dependencies>
<!-- Spring and Spring Boot dependencies -->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId> <artifactId>spring-boot-starter-actuator</artifactId>
@ -72,12 +71,12 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<!-- Workaround for AOT issue (https://github.com/spring-projects/spring-framework/pull/33949) -->
<groupId>io.projectreactor</groupId> <groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId> <artifactId>reactor-core</artifactId>
</dependency> </dependency>
<!-- Databases - Uses H2 by default -->
<dependency> <dependency>
<groupId>com.h2database</groupId> <groupId>com.h2database</groupId>
<artifactId>h2</artifactId> <artifactId>h2</artifactId>
@ -94,7 +93,7 @@
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<!-- Caching -->
<dependency> <dependency>
<groupId>javax.cache</groupId> <groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId> <artifactId>cache-api</artifactId>
@ -104,7 +103,7 @@
<artifactId>caffeine</artifactId> <artifactId>caffeine</artifactId>
</dependency> </dependency>
<!-- Webjars -->
<dependency> <dependency>
<groupId>org.webjars</groupId> <groupId>org.webjars</groupId>
<artifactId>webjars-locator-lite</artifactId> <artifactId>webjars-locator-lite</artifactId>
@ -233,8 +232,7 @@
<artifactId>spring-boot-maven-plugin</artifactId> <artifactId>spring-boot-maven-plugin</artifactId>
<executions> <executions>
<execution> <execution>
<!-- Spring Boot Actuator displays build-related information
if a META-INF/build-info.properties file is present -->
<goals> <goals>
<goal>build-info</goal> <goal>build-info</goal>
</goals> </goals>
@ -269,8 +267,7 @@
</executions> </executions>
</plugin> </plugin>
<!-- Spring Boot Actuator displays build-related information if a git.properties file is
present at the classpath -->
<plugin> <plugin>
<groupId>io.github.git-commit-id</groupId> <groupId>io.github.git-commit-id</groupId>
<artifactId>git-commit-id-maven-plugin</artifactId> <artifactId>git-commit-id-maven-plugin</artifactId>
@ -279,15 +276,14 @@
<failOnUnableToExtractRepoInfo>false</failOnUnableToExtractRepoInfo> <failOnUnableToExtractRepoInfo>false</failOnUnableToExtractRepoInfo>
</configuration> </configuration>
</plugin> </plugin>
<!-- Spring Boot Actuator displays sbom-related information if a CycloneDX SBOM file is
present at the classpath -->
<plugin> <plugin>
<?m2e ignore?>
<groupId>org.cyclonedx</groupId> <groupId>org.cyclonedx</groupId>
<artifactId>cyclonedx-maven-plugin</artifactId> <artifactId>cyclonedx-maven-plugin</artifactId>
</plugin> </plugin>
</plugins> <plugin><groupId>org.pitest</groupId><artifactId>pitest-maven</artifactId><version>1.15.0</version><dependencies><dependency><groupId>org.pitest</groupId><artifactId>pitest-junit5-plugin</artifactId><version>1.2.1</version></dependency></dependencies><configuration><outputFormats>XML</outputFormats><timestampedReports>false</timestampedReports><exportLineCoverage>true</exportLineCoverage></configuration></plugin></plugins>
</build> </build>
<licenses> <licenses>
<license> <license>
@ -347,7 +343,7 @@
<goals> <goals>
<goal>unpack</goal> <goal>unpack</goal>
</goals> </goals>
<?m2e execute onConfiguration,onIncremental?>
<phase>generate-resources</phase> <phase>generate-resources</phase>
<configuration> <configuration>
<artifactItems> <artifactItems>
@ -374,7 +370,7 @@
</configuration> </configuration>
<executions> <executions>
<execution> <execution>
<?m2e execute onConfiguration,onIncremental?>
<goals> <goals>
<goal>compile</goal> <goal>compile</goal>
</goals> </goals>
@ -395,8 +391,7 @@
<build> <build>
<pluginManagement> <pluginManagement>
<plugins> <plugins>
<!-- This plugin's configuration is used to store Eclipse m2e settings
only. It has no influence on the Maven build itself. -->
<plugin> <plugin>
<groupId>org.eclipse.m2e</groupId> <groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId> <artifactId>lifecycle-mapping</artifactId>
@ -414,7 +409,7 @@
</goals> </goals>
</pluginExecutionFilter> </pluginExecutionFilter>
<action> <action>
<ignore></ignore> <ignore />
</action> </action>
</pluginExecution> </pluginExecution>
<pluginExecution> <pluginExecution>
@ -427,7 +422,7 @@
</goals> </goals>
</pluginExecutionFilter> </pluginExecutionFilter>
<action> <action>
<ignore></ignore> <ignore />
</action> </action>
</pluginExecution> </pluginExecution>
<pluginExecution> <pluginExecution>
@ -440,7 +435,7 @@
</goals> </goals>
</pluginExecutionFilter> </pluginExecutionFilter>
<action> <action>
<ignore></ignore> <ignore />
</action> </action>
</pluginExecution> </pluginExecution>
</pluginExecutions> </pluginExecutions>
@ -452,4 +447,4 @@
</build> </build>
</profile> </profile>
</profiles> </profiles>
</project> </project>

View file

@ -14,240 +14,248 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.samples.petclinic.owner; package org.springframework.samples.petclinic.owner;
import org.assertj.core.util.Lists; import org.assertj.core.util.Lists;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledInNativeImage; import org.junit.jupiter.api.condition.DisabledInNativeImage;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.test.context.aot.DisabledInAotMode; import org.springframework.test.context.aot.DisabledInAotMode;
import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.Optional; import java.util.Optional;
import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasProperty; import static org.hamcrest.Matchers.hasProperty;
import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.not;
import static org.mockito.ArgumentMatchers.any; import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.BDDMockito.given; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when; import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
/**
* Test class for {@link OwnerController} /**
* * Test class for {@link OwnerController}
* @author Colin But *
* @author Wick Dynex * Authors: Colin But, Wick Dynex
*/ */
@WebMvcTest(OwnerController.class) @WebMvcTest(OwnerController.class)
@DisabledInNativeImage @DisabledInNativeImage
@DisabledInAotMode @DisabledInAotMode
class OwnerControllerTests { class OwnerControllerTests {
private static final int TEST_OWNER_ID = 1; private static final int TEST_OWNER_ID = 1;
@Autowired @Autowired
private MockMvc mockMvc; private MockMvc mockMvc;
@MockitoBean @MockitoBean
private OwnerRepository owners; private OwnerRepository owners;
private Owner george() { private Owner george() {
Owner george = new Owner(); Owner george = new Owner();
george.setId(TEST_OWNER_ID); george.setId(TEST_OWNER_ID);
george.setFirstName("George"); george.setFirstName("George");
george.setLastName("Franklin"); george.setLastName("Franklin");
george.setAddress("110 W. Liberty St."); george.setAddress("110 W. Liberty St.");
george.setCity("Madison"); george.setCity("Madison");
george.setTelephone("6085551023"); george.setTelephone("6085551023");
Pet max = new Pet(); Pet max = new Pet();
PetType dog = new PetType(); PetType dog = new PetType();
dog.setName("dog"); dog.setName("dog");
max.setType(dog); max.setType(dog);
max.setName("Max"); max.setName("Max");
max.setBirthDate(LocalDate.now()); max.setBirthDate(LocalDate.now());
george.addPet(max); george.addPet(max);
max.setId(1); max.setId(1);
return george; return george;
} }
@BeforeEach @BeforeEach
void setup() { void setup() {
Owner george = george(); Owner george = george();
given(this.owners.findByLastNameStartingWith(eq("Franklin"), any(Pageable.class))) given(this.owners.findByLastNameStartingWith(eq("Franklin"), any(Pageable.class)))
.willReturn(new PageImpl<>(Lists.newArrayList(george))); .willReturn(new PageImpl<>(Lists.newArrayList(george)));
given(this.owners.findAll(any(Pageable.class))).willReturn(new PageImpl<>(Lists.newArrayList(george))); given(this.owners.findAll(any(Pageable.class))).willReturn(new PageImpl<>(Lists.newArrayList(george)));
given(this.owners.findById(TEST_OWNER_ID)).willReturn(Optional.of(george)); given(this.owners.findById(TEST_OWNER_ID)).willReturn(Optional.of(george));
Visit visit = new Visit(); Visit visit = new Visit();
visit.setDate(LocalDate.now()); visit.setDate(LocalDate.now());
george.getPet("Max").getVisits().add(visit); george.getPet("Max").getVisits().add(visit);
} // Added assertion to cover the branch where the pet is not found
assertNull(george.getPet("NonExistent"), "Expected getPet to return null when pet is not found");
@Test }
void testInitCreationForm() throws Exception {
mockMvc.perform(get("/owners/new")) @Test
.andExpect(status().isOk()) void testInitCreationForm() throws Exception {
.andExpect(model().attributeExists("owner")) mockMvc.perform(get("/owners/new"))
.andExpect(view().name("owners/createOrUpdateOwnerForm")); .andExpect(status().isOk())
} .andExpect(model().attributeExists("owner"))
.andExpect(view().name("owners/createOrUpdateOwnerForm"));
@Test }
void testProcessCreationFormSuccess() throws Exception {
mockMvc @Test
.perform(post("/owners/new").param("firstName", "Joe") void testProcessCreationFormSuccess() throws Exception {
.param("lastName", "Bloggs") mockMvc
.param("address", "123 Caramel Street") .perform(post("/owners/new").param("firstName", "Joe")
.param("city", "London") .param("lastName", "Bloggs")
.param("telephone", "1316761638")) .param("address", "123 Caramel Street")
.andExpect(status().is3xxRedirection()); .param("city", "London")
} .param("telephone", "1316761638"))
.andExpect(status().is3xxRedirection());
@Test }
void testProcessCreationFormHasErrors() throws Exception {
mockMvc @Test
.perform(post("/owners/new").param("firstName", "Joe").param("lastName", "Bloggs").param("city", "London")) void testProcessCreationFormHasErrors() throws Exception {
.andExpect(status().isOk()) mockMvc
.andExpect(model().attributeHasErrors("owner")) .perform(post("/owners/new").param("firstName", "Joe").param("lastName", "Bloggs").param("city", "London"))
.andExpect(model().attributeHasFieldErrors("owner", "address")) .andExpect(status().isOk())
.andExpect(model().attributeHasFieldErrors("owner", "telephone")) .andExpect(model().attributeHasErrors("owner"))
.andExpect(view().name("owners/createOrUpdateOwnerForm")); .andExpect(model().attributeHasFieldErrors("owner", "address"))
} .andExpect(model().attributeHasFieldErrors("owner", "telephone"))
.andExpect(view().name("owners/createOrUpdateOwnerForm"));
@Test }
void testInitFindForm() throws Exception {
mockMvc.perform(get("/owners/find")) @Test
.andExpect(status().isOk()) void testInitFindForm() throws Exception {
.andExpect(model().attributeExists("owner")) mockMvc.perform(get("/owners/find"))
.andExpect(view().name("owners/findOwners")); .andExpect(status().isOk())
} .andExpect(model().attributeExists("owner"))
.andExpect(view().name("owners/findOwners"));
@Test }
void testProcessFindFormSuccess() throws Exception {
Page<Owner> tasks = new PageImpl<>(Lists.newArrayList(george(), new Owner())); @Test
when(this.owners.findByLastNameStartingWith(anyString(), any(Pageable.class))).thenReturn(tasks); void testProcessFindFormSuccess() throws Exception {
mockMvc.perform(get("/owners?page=1")).andExpect(status().isOk()).andExpect(view().name("owners/ownersList")); Page<Owner> tasks = new PageImpl<>(Lists.newArrayList(george(), new Owner()));
} when(this.owners.findByLastNameStartingWith(anyString(), any(Pageable.class))).thenReturn(tasks);
mockMvc.perform(get("/owners?page=1")).andExpect(status().isOk()).andExpect(view().name("owners/ownersList"));
@Test }
void testProcessFindFormByLastName() throws Exception {
Page<Owner> tasks = new PageImpl<>(Lists.newArrayList(george())); @Test
when(this.owners.findByLastNameStartingWith(eq("Franklin"), any(Pageable.class))).thenReturn(tasks); void testProcessFindFormByLastName() throws Exception {
mockMvc.perform(get("/owners?page=1").param("lastName", "Franklin")) Page<Owner> tasks = new PageImpl<>(Lists.newArrayList(george()));
.andExpect(status().is3xxRedirection()) when(this.owners.findByLastNameStartingWith(eq("Franklin"), any(Pageable.class))).thenReturn(tasks);
.andExpect(view().name("redirect:/owners/" + TEST_OWNER_ID)); 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 {
Page<Owner> tasks = new PageImpl<>(Lists.newArrayList()); @Test
when(this.owners.findByLastNameStartingWith(eq("Unknown Surname"), any(Pageable.class))).thenReturn(tasks); void testProcessFindFormNoOwnersFound() throws Exception {
mockMvc.perform(get("/owners?page=1").param("lastName", "Unknown Surname")) Page<Owner> tasks = new PageImpl<>(Lists.newArrayList());
.andExpect(status().isOk()) when(this.owners.findByLastNameStartingWith(eq("Unknown Surname"), any(Pageable.class))).thenReturn(tasks);
.andExpect(model().attributeHasFieldErrors("owner", "lastName")) mockMvc.perform(get("/owners?page=1").param("lastName", "Unknown Surname"))
.andExpect(model().attributeHasFieldErrorCode("owner", "lastName", "notFound")) .andExpect(status().isOk())
.andExpect(view().name("owners/findOwners")); .andExpect(model().attributeHasFieldErrors("owner", "lastName"))
.andExpect(model().attributeHasFieldErrorCode("owner", "lastName", "notFound"))
} .andExpect(view().name("owners/findOwners"));
@Test }
void testInitUpdateOwnerForm() throws Exception {
mockMvc.perform(get("/owners/{ownerId}/edit", TEST_OWNER_ID)) @Test
.andExpect(status().isOk()) void testInitUpdateOwnerForm() throws Exception {
.andExpect(model().attributeExists("owner")) mockMvc.perform(get("/owners/{ownerId}/edit", TEST_OWNER_ID))
.andExpect(model().attribute("owner", hasProperty("lastName", is("Franklin")))) .andExpect(status().isOk())
.andExpect(model().attribute("owner", hasProperty("firstName", is("George")))) .andExpect(model().attributeExists("owner"))
.andExpect(model().attribute("owner", hasProperty("address", is("110 W. Liberty St.")))) .andExpect(model().attribute("owner", hasProperty("lastName", is("Franklin"))))
.andExpect(model().attribute("owner", hasProperty("city", is("Madison")))) .andExpect(model().attribute("owner", hasProperty("firstName", is("George"))))
.andExpect(model().attribute("owner", hasProperty("telephone", is("6085551023")))) .andExpect(model().attribute("owner", hasProperty("address", is("110 W. Liberty St."))))
.andExpect(view().name("owners/createOrUpdateOwnerForm")); .andExpect(model().attribute("owner", hasProperty("city", is("Madison"))))
} .andExpect(model().attribute("owner", hasProperty("telephone", is("6085551023"))))
.andExpect(view().name("owners/createOrUpdateOwnerForm"));
@Test }
void testProcessUpdateOwnerFormSuccess() throws Exception {
mockMvc @Test
.perform(post("/owners/{ownerId}/edit", TEST_OWNER_ID).param("firstName", "Joe") void testProcessUpdateOwnerFormSuccess() throws Exception {
.param("lastName", "Bloggs") mockMvc
.param("address", "123 Caramel Street") .perform(post("/owners/{ownerId}/edit", TEST_OWNER_ID).param("firstName", "Joe")
.param("city", "London") .param("lastName", "Bloggs")
.param("telephone", "1616291589")) .param("address", "123 Caramel Street")
.andExpect(status().is3xxRedirection()) .param("city", "London")
.andExpect(view().name("redirect:/owners/{ownerId}")); .param("telephone", "1616291589"))
} .andExpect(status().is3xxRedirection())
.andExpect(view().name("redirect:/owners/{ownerId}"));
@Test }
void testProcessUpdateOwnerFormUnchangedSuccess() throws Exception {
mockMvc.perform(post("/owners/{ownerId}/edit", TEST_OWNER_ID)) @Test
.andExpect(status().is3xxRedirection()) void testProcessUpdateOwnerFormUnchangedSuccess() throws Exception {
.andExpect(view().name("redirect:/owners/{ownerId}")); mockMvc.perform(post("/owners/{ownerId}/edit", TEST_OWNER_ID))
} .andExpect(status().is3xxRedirection())
.andExpect(view().name("redirect:/owners/{ownerId}"));
@Test }
void testProcessUpdateOwnerFormHasErrors() throws Exception {
mockMvc @Test
.perform(post("/owners/{ownerId}/edit", TEST_OWNER_ID).param("firstName", "Joe") void testProcessUpdateOwnerFormHasErrors() throws Exception {
.param("lastName", "Bloggs") mockMvc
.param("address", "") .perform(post("/owners/{ownerId}/edit", TEST_OWNER_ID).param("firstName", "Joe")
.param("telephone", "")) .param("lastName", "Bloggs")
.andExpect(status().isOk()) .param("address", "")
.andExpect(model().attributeHasErrors("owner")) .param("telephone", ""))
.andExpect(model().attributeHasFieldErrors("owner", "address")) .andExpect(status().isOk())
.andExpect(model().attributeHasFieldErrors("owner", "telephone")) .andExpect(model().attributeHasErrors("owner"))
.andExpect(view().name("owners/createOrUpdateOwnerForm")); .andExpect(model().attributeHasFieldErrors("owner", "address"))
} .andExpect(model().attributeHasFieldErrors("owner", "telephone"))
.andExpect(view().name("owners/createOrUpdateOwnerForm"));
@Test }
void testShowOwner() throws Exception {
mockMvc.perform(get("/owners/{ownerId}", TEST_OWNER_ID)) @Test
.andExpect(status().isOk()) void testShowOwner() throws Exception {
.andExpect(model().attribute("owner", hasProperty("lastName", is("Franklin")))) mockMvc.perform(get("/owners/{ownerId}", TEST_OWNER_ID))
.andExpect(model().attribute("owner", hasProperty("firstName", is("George")))) .andExpect(status().isOk())
.andExpect(model().attribute("owner", hasProperty("address", is("110 W. Liberty St.")))) .andExpect(model().attribute("owner", hasProperty("lastName", is("Franklin"))))
.andExpect(model().attribute("owner", hasProperty("city", is("Madison")))) .andExpect(model().attribute("owner", hasProperty("firstName", is("George"))))
.andExpect(model().attribute("owner", hasProperty("telephone", is("6085551023")))) .andExpect(model().attribute("owner", hasProperty("address", is("110 W. Liberty St."))))
.andExpect(model().attribute("owner", hasProperty("pets", not(empty())))) .andExpect(model().attribute("owner", hasProperty("city", is("Madison"))))
.andExpect(model().attribute("owner", .andExpect(model().attribute("owner", hasProperty("telephone", is("6085551023"))))
hasProperty("pets", hasItem(hasProperty("visits", hasSize(greaterThan(0))))))) .andExpect(model().attribute("owner", hasProperty("pets", not(empty()))))
.andExpect(view().name("owners/ownerDetails")); .andExpect(model().attribute("owner",
} hasProperty("pets", hasItem(hasProperty("visits", hasSize(greaterThan(0)))))))
.andExpect(view().name("owners/ownerDetails"))
@Test .andExpect(result -> {
public void testProcessUpdateOwnerFormWithIdMismatch() throws Exception { Owner owner = (Owner) result.getModelAndView().getModel().get("owner");
int pathOwnerId = 1; // New assertion to exercise the negative branch of getPet
assertNull(owner.getPet("NonExistent"), "Expected getPet to return null when pet is not found");
Owner owner = new Owner(); });
owner.setId(2); }
owner.setFirstName("John");
owner.setLastName("Doe"); @Test
owner.setAddress("Center Street"); public void testProcessUpdateOwnerFormWithIdMismatch() throws Exception {
owner.setCity("New York"); int pathOwnerId = 1;
owner.setTelephone("0123456789");
Owner owner = new Owner();
when(owners.findById(pathOwnerId)).thenReturn(Optional.of(owner)); owner.setId(2);
owner.setFirstName("John");
mockMvc.perform(MockMvcRequestBuilders.post("/owners/{ownerId}/edit", pathOwnerId).flashAttr("owner", owner)) owner.setLastName("Doe");
.andExpect(status().is3xxRedirection()) owner.setAddress("Center Street");
.andExpect(redirectedUrl("/owners/" + pathOwnerId + "/edit")) owner.setCity("New York");
.andExpect(flash().attributeExists("error")); owner.setTelephone("0123456789");
}
when(owners.findById(pathOwnerId)).thenReturn(Optional.of(owner));
}
mockMvc.perform(MockMvcRequestBuilders.post("/owners/{ownerId}/edit", pathOwnerId).flashAttr("owner", owner))
.andExpect(status().is3xxRedirection())
.andExpect(redirectedUrl("/owners/" + pathOwnerId + "/edit"))
.andExpect(flash().attributeExists("error"));
}
}