(1). 需求
随着微服务的流行,前后端分离,这时候,后端不再像以前一样,可以有一界面进行测试了,随之开发人员的代码质量也出现了问题.
那么,如何解决呢?
解决方案是,开发人员应该写单元测试,单元测试的目的是让测试用例尽可能的覆盖到分支上,然后,在测试时要关闭类与类之间的依赖关系.
(2). User
package help.lixin.test.model;
import java.io.Serializable;
public class User implements Serializable {
private static final long serialVersionUID = 4953463227600668630L;
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User [name=" + name + ", age=" + age + "]";
}
}
(3). HelloController
package help.lixin.test.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import help.lixin.test.model.User;
import help.lixin.test.service.IHelloService;
@RestController
public class HelloController {
@Autowired
private IHelloService helloService;
@GetMapping("/")
public String home() {
System.err.println("===========/home===================");
return "Hello World!";
}
@GetMapping("/var1")
public String var1(@RequestParam("name") String name) {
System.err.println("===========/var1===================");
return "Hello World VAR1!";
}
@PostMapping("/var2")
public String var2(@RequestParam("name") String name) {
System.err.println("===========/var2===================");
return "Hello World VAR2!";
}
@PostMapping("/json")
public User json(@RequestBody User user) {
// 调了业务接口
helloService.sayhello();
System.err.println("===========/var json===================");
User result = new User();
result.setName("LIXIN");
result.setAge(user.getAge());
return result;
}
}
(4). IHelloService
package help.lixin.test.service;
public interface IHelloService {
public void sayhello();
}
(5). HelloService
package help.lixin.test.service.impl;
import org.springframework.stereotype.Service;
import help.lixin.test.service.IHelloService;
@Service
public class HelloService implements IHelloService {
public void sayhello() {
System.out.println("=========================hello world=========================");
}
}
(6). LogFilter
package help.lixin.test.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class LogFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("**********************LogFilter start******************************");
chain.doFilter(request, response);
System.out.println("**********************LogFilter end******************************");
}
}
(7). TestConfig
package help.lixin.test;
import javax.servlet.Filter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import help.lixin.test.filter.LogFilter;
@Configuration
public class TestConfig {
@Bean
public Filter logFilter() {
return new LogFilter();
}
@Bean
public FilterRegistrationBean filterRegistrationBean(Filter logFilter){
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(logFilter);
bean.addUrlPatterns("/*");
return bean;
}
}
(8). WebMvcTest
package help.lixin.test;
import static org.hamcrest.Matchers.containsString;
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.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.mockito.Mockito.*;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import com.fasterxml.jackson.databind.ObjectMapper;
import help.lixin.test.controller.HelloController;
import help.lixin.test.model.User;
import help.lixin.test.service.IHelloService;
// @RequestBody时需要加入:@EnableWebMvc,否则出现405请求
@EnableWebMvc
@RunWith(SpringRunner.class)
// 配置仅仅针对哪个类进行测试
@SpringBootTest(classes = { HelloController.class ,TestConfig.class })
@AutoConfigureMockMvc
public class WebMvcTest {
@Autowired
private MockMvc mockMvc;
@MockBean
// 创建一个虚拟的Bean
private IHelloService helloService;
private ObjectMapper objectMapper;
@Before
public void init() {
// 如果自动注入,则需要配合:@AutoConfigureMockMvc注解
// 否则,需要像下面这样手写.
// mockMvc = MockMvcBuilders.standaloneSetup(new HelloController()).build();
objectMapper = new ObjectMapper();
}
@Test
public void testHome() throws Exception {
mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk())
.andExpect(content().string(containsString("Hello World!")));
}
@Test
public void tetsVar1() throws Exception {
mockMvc.perform(get("/var1?name=", "zhangsan")).andDo(print()).andExpect(status().isOk())
.andExpect(content().string(containsString("Hello World VAR1!")));
}
@Test
public void tetsVar2() throws Exception {
mockMvc.perform(post("/var2").param("name", "zhansan")).andDo(print()).andExpect(status().isOk())
.andExpect(content().string(containsString("Hello World VAR2!")));
}
@Test
public void testJson() throws Exception {
// 无参无返回值处理方法的Mock
doNothing().when(helloService).sayhello();
User param = new User();
param.setAge(34);
param.setName("ZHANGSAN");
String requestString = objectMapper.writeValueAsString(param);
// JSON请求时,需要加上注解:@EnableWebMvc
mockMvc.perform(post("/json").contentType(MediaType.APPLICATION_JSON_UTF8_VALUE).content(requestString))
.andDo(print()).andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.name").value("LIXIN"));
}
}
(7). pom.xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix</artifactId>
<version>2.1.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
(8). 总结
通过MockMvc可以解决对Controller测试(因为依赖容器嘛),而:@MockBean可以解决类与类之间的关系.
“mockito学习参考”