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

47
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>

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 * Authors: Colin But, Wick Dynex
* @author 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 @Test
void testInitCreationForm() throws Exception { void testInitCreationForm() throws Exception {
mockMvc.perform(get("/owners/new")) mockMvc.perform(get("/owners/new"))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(model().attributeExists("owner")) .andExpect(model().attributeExists("owner"))
.andExpect(view().name("owners/createOrUpdateOwnerForm")); .andExpect(view().name("owners/createOrUpdateOwnerForm"));
} }
@Test @Test
void testProcessCreationFormSuccess() throws Exception { void testProcessCreationFormSuccess() throws Exception {
mockMvc mockMvc
.perform(post("/owners/new").param("firstName", "Joe") .perform(post("/owners/new").param("firstName", "Joe")
.param("lastName", "Bloggs") .param("lastName", "Bloggs")
.param("address", "123 Caramel Street") .param("address", "123 Caramel Street")
.param("city", "London") .param("city", "London")
.param("telephone", "1316761638")) .param("telephone", "1316761638"))
.andExpect(status().is3xxRedirection()); .andExpect(status().is3xxRedirection());
} }
@Test @Test
void testProcessCreationFormHasErrors() throws Exception { void testProcessCreationFormHasErrors() throws Exception {
mockMvc mockMvc
.perform(post("/owners/new").param("firstName", "Joe").param("lastName", "Bloggs").param("city", "London")) .perform(post("/owners/new").param("firstName", "Joe").param("lastName", "Bloggs").param("city", "London"))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(model().attributeHasErrors("owner")) .andExpect(model().attributeHasErrors("owner"))
.andExpect(model().attributeHasFieldErrors("owner", "address")) .andExpect(model().attributeHasFieldErrors("owner", "address"))
.andExpect(model().attributeHasFieldErrors("owner", "telephone")) .andExpect(model().attributeHasFieldErrors("owner", "telephone"))
.andExpect(view().name("owners/createOrUpdateOwnerForm")); .andExpect(view().name("owners/createOrUpdateOwnerForm"));
} }
@Test @Test
void testInitFindForm() throws Exception { void testInitFindForm() throws Exception {
mockMvc.perform(get("/owners/find")) mockMvc.perform(get("/owners/find"))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(model().attributeExists("owner")) .andExpect(model().attributeExists("owner"))
.andExpect(view().name("owners/findOwners")); .andExpect(view().name("owners/findOwners"));
} }
@Test @Test
void testProcessFindFormSuccess() throws Exception { void testProcessFindFormSuccess() throws Exception {
Page<Owner> tasks = new PageImpl<>(Lists.newArrayList(george(), new Owner())); Page<Owner> tasks = new PageImpl<>(Lists.newArrayList(george(), new Owner()));
when(this.owners.findByLastNameStartingWith(anyString(), any(Pageable.class))).thenReturn(tasks); when(this.owners.findByLastNameStartingWith(anyString(), any(Pageable.class))).thenReturn(tasks);
mockMvc.perform(get("/owners?page=1")).andExpect(status().isOk()).andExpect(view().name("owners/ownersList")); mockMvc.perform(get("/owners?page=1")).andExpect(status().isOk()).andExpect(view().name("owners/ownersList"));
} }
@Test @Test
void testProcessFindFormByLastName() throws Exception { void testProcessFindFormByLastName() throws Exception {
Page<Owner> tasks = new PageImpl<>(Lists.newArrayList(george())); Page<Owner> tasks = new PageImpl<>(Lists.newArrayList(george()));
when(this.owners.findByLastNameStartingWith(eq("Franklin"), any(Pageable.class))).thenReturn(tasks); when(this.owners.findByLastNameStartingWith(eq("Franklin"), any(Pageable.class))).thenReturn(tasks);
mockMvc.perform(get("/owners?page=1").param("lastName", "Franklin")) mockMvc.perform(get("/owners?page=1").param("lastName", "Franklin"))
.andExpect(status().is3xxRedirection()) .andExpect(status().is3xxRedirection())
.andExpect(view().name("redirect:/owners/" + TEST_OWNER_ID)); .andExpect(view().name("redirect:/owners/" + TEST_OWNER_ID));
} }
@Test @Test
void testProcessFindFormNoOwnersFound() throws Exception { void testProcessFindFormNoOwnersFound() throws Exception {
Page<Owner> tasks = new PageImpl<>(Lists.newArrayList()); Page<Owner> tasks = new PageImpl<>(Lists.newArrayList());
when(this.owners.findByLastNameStartingWith(eq("Unknown Surname"), any(Pageable.class))).thenReturn(tasks); when(this.owners.findByLastNameStartingWith(eq("Unknown Surname"), any(Pageable.class))).thenReturn(tasks);
mockMvc.perform(get("/owners?page=1").param("lastName", "Unknown Surname")) mockMvc.perform(get("/owners?page=1").param("lastName", "Unknown Surname"))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(model().attributeHasFieldErrors("owner", "lastName")) .andExpect(model().attributeHasFieldErrors("owner", "lastName"))
.andExpect(model().attributeHasFieldErrorCode("owner", "lastName", "notFound")) .andExpect(model().attributeHasFieldErrorCode("owner", "lastName", "notFound"))
.andExpect(view().name("owners/findOwners")); .andExpect(view().name("owners/findOwners"));
} }
@Test @Test
void testInitUpdateOwnerForm() throws Exception { void testInitUpdateOwnerForm() throws Exception {
mockMvc.perform(get("/owners/{ownerId}/edit", TEST_OWNER_ID)) mockMvc.perform(get("/owners/{ownerId}/edit", TEST_OWNER_ID))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(model().attributeExists("owner")) .andExpect(model().attributeExists("owner"))
.andExpect(model().attribute("owner", hasProperty("lastName", is("Franklin")))) .andExpect(model().attribute("owner", hasProperty("lastName", is("Franklin"))))
.andExpect(model().attribute("owner", hasProperty("firstName", is("George")))) .andExpect(model().attribute("owner", hasProperty("firstName", is("George"))))
.andExpect(model().attribute("owner", hasProperty("address", is("110 W. Liberty St.")))) .andExpect(model().attribute("owner", hasProperty("address", is("110 W. Liberty St."))))
.andExpect(model().attribute("owner", hasProperty("city", is("Madison")))) .andExpect(model().attribute("owner", hasProperty("city", is("Madison"))))
.andExpect(model().attribute("owner", hasProperty("telephone", is("6085551023")))) .andExpect(model().attribute("owner", hasProperty("telephone", is("6085551023"))))
.andExpect(view().name("owners/createOrUpdateOwnerForm")); .andExpect(view().name("owners/createOrUpdateOwnerForm"));
} }
@Test @Test
void testProcessUpdateOwnerFormSuccess() throws Exception { void testProcessUpdateOwnerFormSuccess() throws Exception {
mockMvc mockMvc
.perform(post("/owners/{ownerId}/edit", TEST_OWNER_ID).param("firstName", "Joe") .perform(post("/owners/{ownerId}/edit", TEST_OWNER_ID).param("firstName", "Joe")
.param("lastName", "Bloggs") .param("lastName", "Bloggs")
.param("address", "123 Caramel Street") .param("address", "123 Caramel Street")
.param("city", "London") .param("city", "London")
.param("telephone", "1616291589")) .param("telephone", "1616291589"))
.andExpect(status().is3xxRedirection()) .andExpect(status().is3xxRedirection())
.andExpect(view().name("redirect:/owners/{ownerId}")); .andExpect(view().name("redirect:/owners/{ownerId}"));
} }
@Test @Test
void testProcessUpdateOwnerFormUnchangedSuccess() throws Exception { void testProcessUpdateOwnerFormUnchangedSuccess() throws Exception {
mockMvc.perform(post("/owners/{ownerId}/edit", TEST_OWNER_ID)) mockMvc.perform(post("/owners/{ownerId}/edit", TEST_OWNER_ID))
.andExpect(status().is3xxRedirection()) .andExpect(status().is3xxRedirection())
.andExpect(view().name("redirect:/owners/{ownerId}")); .andExpect(view().name("redirect:/owners/{ownerId}"));
} }
@Test @Test
void testProcessUpdateOwnerFormHasErrors() throws Exception { void testProcessUpdateOwnerFormHasErrors() throws Exception {
mockMvc mockMvc
.perform(post("/owners/{ownerId}/edit", TEST_OWNER_ID).param("firstName", "Joe") .perform(post("/owners/{ownerId}/edit", TEST_OWNER_ID).param("firstName", "Joe")
.param("lastName", "Bloggs") .param("lastName", "Bloggs")
.param("address", "") .param("address", "")
.param("telephone", "")) .param("telephone", ""))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(model().attributeHasErrors("owner")) .andExpect(model().attributeHasErrors("owner"))
.andExpect(model().attributeHasFieldErrors("owner", "address")) .andExpect(model().attributeHasFieldErrors("owner", "address"))
.andExpect(model().attributeHasFieldErrors("owner", "telephone")) .andExpect(model().attributeHasFieldErrors("owner", "telephone"))
.andExpect(view().name("owners/createOrUpdateOwnerForm")); .andExpect(view().name("owners/createOrUpdateOwnerForm"));
} }
@Test @Test
void testShowOwner() throws Exception { void testShowOwner() throws Exception {
mockMvc.perform(get("/owners/{ownerId}", TEST_OWNER_ID)) mockMvc.perform(get("/owners/{ownerId}", TEST_OWNER_ID))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(model().attribute("owner", hasProperty("lastName", is("Franklin")))) .andExpect(model().attribute("owner", hasProperty("lastName", is("Franklin"))))
.andExpect(model().attribute("owner", hasProperty("firstName", is("George")))) .andExpect(model().attribute("owner", hasProperty("firstName", is("George"))))
.andExpect(model().attribute("owner", hasProperty("address", is("110 W. Liberty St.")))) .andExpect(model().attribute("owner", hasProperty("address", is("110 W. Liberty St."))))
.andExpect(model().attribute("owner", hasProperty("city", is("Madison")))) .andExpect(model().attribute("owner", hasProperty("city", is("Madison"))))
.andExpect(model().attribute("owner", hasProperty("telephone", is("6085551023")))) .andExpect(model().attribute("owner", hasProperty("telephone", is("6085551023"))))
.andExpect(model().attribute("owner", hasProperty("pets", not(empty())))) .andExpect(model().attribute("owner", hasProperty("pets", not(empty()))))
.andExpect(model().attribute("owner", .andExpect(model().attribute("owner",
hasProperty("pets", hasItem(hasProperty("visits", hasSize(greaterThan(0))))))) hasProperty("pets", hasItem(hasProperty("visits", hasSize(greaterThan(0)))))))
.andExpect(view().name("owners/ownerDetails")); .andExpect(view().name("owners/ownerDetails"))
} .andExpect(result -> {
Owner owner = (Owner) result.getModelAndView().getModel().get("owner");
// New assertion to exercise the negative branch of getPet
assertNull(owner.getPet("NonExistent"), "Expected getPet to return null when pet is not found");
});
}
@Test @Test
public void testProcessUpdateOwnerFormWithIdMismatch() throws Exception { public void testProcessUpdateOwnerFormWithIdMismatch() throws Exception {
int pathOwnerId = 1; int pathOwnerId = 1;
Owner owner = new Owner(); Owner owner = new Owner();
owner.setId(2); owner.setId(2);
owner.setFirstName("John"); owner.setFirstName("John");
owner.setLastName("Doe"); owner.setLastName("Doe");
owner.setAddress("Center Street"); owner.setAddress("Center Street");
owner.setCity("New York"); owner.setCity("New York");
owner.setTelephone("0123456789"); owner.setTelephone("0123456789");
when(owners.findById(pathOwnerId)).thenReturn(Optional.of(owner)); when(owners.findById(pathOwnerId)).thenReturn(Optional.of(owner));
mockMvc.perform(MockMvcRequestBuilders.post("/owners/{ownerId}/edit", pathOwnerId).flashAttr("owner", owner)) mockMvc.perform(MockMvcRequestBuilders.post("/owners/{ownerId}/edit", pathOwnerId).flashAttr("owner", owner))
.andExpect(status().is3xxRedirection()) .andExpect(status().is3xxRedirection())
.andExpect(redirectedUrl("/owners/" + pathOwnerId + "/edit")) .andExpect(redirectedUrl("/owners/" + pathOwnerId + "/edit"))
.andExpect(flash().attributeExists("error")); .andExpect(flash().attributeExists("error"));
} }
}
}