我是Spring MVC的新手,目前正在编写一个@Controller类,但其中没有任何方法具有业务逻辑,更不用说在'/static/'下的视图HTML文件了。首先,我想看看如何对每个方法进行单元测试,以确保在插入业务逻辑之前所有端点都响应200/ok,你知道的,测试驱动开发。然后,当我对一个带有@ModelAttribute分配的@PostMapping注释方法进行单元测试时遇到了困难。昨天经过我的整个搜索,我为某人组合了代码,以单元测试涉及@PostMapping和@ModelAttribute的这种情况,在其中您需要在'post'方法上更新模型属性的参数值。我非常欢迎积极的反馈,以使我的测试更好,只是想在这种情况下发布此内容,以便其他新手也可以测试并确保在@ModelAttribute中的post之后保存新信息,而无需为独立单元测试查看html/jsp文件视图,请参考@Controller2和String updateQuoteRequest()方法,并查看类QuoteRequestManagementController_UnitTests中的最后一个测试以获取更多详细信息。
pom.xml文件:
<?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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>spring-mvc-hotel-app</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>serving-web-content</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
模型属性类:
package com.corplithotel.eventsapp.domain;
public class QuoteRequest {
String customer;
String age;
String budget;
String eventType;
String foodAllergies;
public String getCustomer() {
return customer;
}
public void setCustomer(String customer) {
this.customer = customer;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getBudget() {
return budget;
}
public void setBudget(String budget) {
this.budget = budget;
}
public String getEventType() {
return eventType;
}
public void setEventType(String eventType) {
this.eventType = eventType;
}
public String getFoodAllergies() {
return foodAllergies;
}
public void setFoodAllergies(String foodAllergies) {
this.foodAllergies = foodAllergies;
}
}
主类:
package com.corplithotel.eventsapp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class CorpLitHotel {
public static void main(String[] args) {
SpringApplication.run(CorpLitHotel.class, args);
}
}
@控制器1
package com.corplithotel.eventsapp.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import com.corplithotel.eventsapp.domain.QuoteRequest;
@Controller
public class QuoteRequestController {
@GetMapping("/newquote")
public String beginQuoteRequest(Model model) {
return "newQuote";
}
@PostMapping("/newquote")
public String submitQuoteRequest(@ModelAttribute QuoteRequest formBean) {
return "newQuoteConfirmation";
}
}
控制器1单元测试:
package com.corplithotel.eventsapp.controller;
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 org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import com.corplithotel.eventsapp.domain.QuoteRequest;
@ExtendWith(MockitoExtension.class)
@WebMvcTest( QuoteRequestController.class)
@TestInstance(Lifecycle.PER_CLASS)
public class QuoteRequestController_UnitTests {
@Mock
private WebApplicationContext wac;
@InjectMocks
private QuoteRequestController qrc;
private MockMvc qrcMockMvc;
@BeforeAll
public void setUp() {
qrcMockMvc = MockMvcBuilders.standaloneSetup(qrc).build();
}
@Test
@DisplayName("testGetQuoteForm.. beginQuoteRequest().. Expected to pass..")
public void testGetQuoteForm() throws Exception {
qrcMockMvc
.perform(get("/newquote"))
.andExpect(status().is(200))
.andReturn();
}
@Test
@DisplayName("testPostQuoteForm().. submitQuoteRequest.. Expected to pass..")
public void testPostQuoteForm() throws Exception {
QuoteRequest aFormBean = new QuoteRequest();
qrcMockMvc
.perform(post("/newquote", aFormBean))
.andExpect(status().isOk())
.andReturn();
}
}
结果 1:
Junit 控制器 1 结果
控制器 2:
package com.corplithotel.eventsapp.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
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.RequestParam;
import com.corplithotel.eventsapp.domain.QuoteRequest;
@Controller
public class QuoteRequestManagementController {
@GetMapping(path = "/quoteRequests")
public String listQuoteRequests() {
return "quoteRequestsList";
}
@GetMapping(path = "/quoteRequests", params="eventType=wedding")
public String listWeddingRequests() {
return "quoteWeddingRequestsList";
}
@GetMapping(path = "/quoteRequests", params="eventType=birthday")
public String listBirthdayRequests() {
return "quoteBirthdayRequestsList";
}
@GetMapping(path = "/quoteRequests", params="eventType")
public String listAllEventTypeRequests() {
return "quoteAllEventTypeRequestList";
}
@GetMapping(path = "/quoteRequests", params="!eventType")
public String listNoneEventTypeRequests() {
return "quoteNoneEventTypeRequestsList";
}
@GetMapping("/quoteRequests/{quoteID}")
public String viewQuoteRequest(@PathVariable int quoteID) {
return "quoteRequestsDetails";
}
@PostMapping ("/quoteUpdateDetails")
public String updateQuoteRequest(
@ModelAttribute("quoteRequest") QuoteRequest quoteRequest) {
return "quoteUpdateDetails";
}
}
控制器 2 单元测试:
package com.corplithotel.eventsapp.controller;
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 org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.web.servlet.MockMvc;
import
org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import com.corplithotel.eventsapp.domain.QuoteRequest;
@ExtendWith(MockitoExtension.class)
@WebMvcTest( QuoteRequestManagementController.class)
@TestInstance(Lifecycle.PER_CLASS)
class QuoteRequestManagementController_UnitTests {
@Mock
private WebApplicationContext wac;
@InjectMocks
private QuoteRequestManagementController qrmc;
private MockMvc qrmcMockMvc;
@BeforeAll
public void setUp() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/");
viewResolver.setSuffix(".html");
qrmcMockMvc=
MockMvcBuilders.standaloneSetup(qrmc)
.setViewResolvers(viewResolver).build();
}
@Test
@DisplayName("testListQuoteRequests().. Test should pass")
void testlistQuoteRequests() throws Exception {
qrmcMockMvc
.perform(get("/quoteRequests"))
.andExpect(status().is(200))
.andReturn();
}
@Test
@DisplayName("testListWeddingRequests() .. Parameter Of A Specific Value Test1.. Test should pass")
void testlistWeddingRequests() throws Exception {
qrmcMockMvc
.perform(get("/quoteRequests?eventType=wedding"))
.andExpect(status().is(200))
.andReturn();
}
@Test
@DisplayName("testListBirthdayRequests() .. Parameter Of A Specific Value Test2.. Test should pass")
void testlistBirthdayRequests() throws Exception {
qrmcMockMvc
.perform(get("/quoteRequests?eventType=birthday"))
.andExpect(status().is(200))
.andReturn();
}
@Test
@DisplayName("testListAllEventsRequests() .. Parameter with no specified value.. Test should pass")
void testlistAllEventsRequests() throws Exception {
qrmcMockMvc
.perform(get("/quoteRequests?eventType"))
.andExpect(status().is(200))
.andReturn();
}
@Test
@DisplayName("testNoneEventTypeRequests() .. no parameter .. Test should pass")
void testNoneEventTypeRequests() throws Exception {
qrmcMockMvc
.perform(get("/quoteRequests?!eventType"))
.andExpect(status().is(200))
.andReturn();
}
@Test
@DisplayName("testViewQuoteRequest().. by 'quoteID'.. Test should pass")
void testViewQuoteRequest() throws Exception {
qrmcMockMvc
.perform(get("/quoteRequests/{quoteID}", 4))
.andExpect(status().is(200))
.andReturn();
}
@Test
@DisplayName("test2ViewQuoteRequest().. by 'quoteID'.. Test should pass")
void tes2tViewQuoteRequest() throws Exception {
qrmcMockMvc
.perform(get("/quoteRequests/{quoteID}", 415))
.andExpect(status().is(200))
.andReturn();
}
@Test
void testupdateQuoteRequest() throws Exception {
MockHttpServletRequestBuilder updateDetails = post("/quoteUpdateDetails")
.param("customer", "Joe")
.param("age", "12")
.param("budget", "$1209")
.param("eventType", "wedding")
.param("foodAllergies", "fish")
.flashAttr("quoteRequest", new QuoteRequest());
qrmcMockMvc
.perform( updateDetails)
.andExpect(status().is(200));
}
}