前段时间胖虎同学问我这边是怎么做的代码自测,想了下,代码自测?想必很多小伙伴说直接用postman测不就ok了么?但是我个人还是比较倾向于在项目中写单元、功能测试用例,测试代码也需要提交到git上,这样后续维护起来比较方便,postman测试团队合作效果较差,目前我这边团队没有代码自测的规范,有的用postman,有的jmeter,有的写单元测试,一个模块功能后续需求有变更,换个同学开发,那之前的测试数据都没有了,需要重新对接口进行配置测试。我还是比较喜欢在项目的Test目录去写相关测试代码,这里来和大家一起总结下SpringBootTest那点事
单元测试一般我们写完接口之后,需要去测试下接口是否ok,有些同学会在项目的test目录下面去创建一个测试类,然后创建一个测试方法,再加上@Test注解,在方法内部用httpclient发起请求,去调用本地启动服务的接口,这种测试方式相当于在本地发起请求调用本地服务的接口,需要引入httpClient依赖,写好get,post,put,delete工具方法,然后直接发起http请求就ok了。
这种测试方式,请求URL可以随时替换,比如由本地环境切换成测试环境,只要服务部署了,我们都可以调用,显然这种切换很方便。但是这种测试也有局限性,就是测试的粒度不好控制,每次测试都是Controller到Service再到Dao,但有些时候我们只是想测试Service里面的某个方法,并不需要测试整个流程。
功能测试前面说到在方法上加上@Test来进行单元测试,这里再来介绍下SpringBootTest ,SpringBootTest 是在Spring Test之上的再次封装,增加了切片测试,增强了mock能力。整体上,SpringBootTest支持的测试种类,大致可以分为如下三类:
单元测试:一般面向方法,编写一般业务代码时,测试成本较大。涉及到的注解有@Test。切片测试:一般面向难于测试的边界功能,介于单元测试和功能测试之间。涉及到的注解有@RunWith @WebMvcTest等。功能测试:一般面向某个完整的业务功能,同时也可以使用切面测试中的mock能力,推荐使用。涉及到的注解有@RunWith @SpringBootTest等。
功能测试过程中的几个关键要素及支撑方式如下:
测试运行环境:通过@RunWith 和 @SpringBootTest启动spring容器。mock能力:Mockito提供了强大mock功能。断言能力:AssertJ、Hamcrest、JsonPath提供了强大的断言能力。
一般我们在创建SpringBoot项目的时候,都会在项目添加下面的测试依赖
org.springframework.boot spring-boot-starter-testtest
一旦依赖了spring-boot-starter-test,下面这些类库将被一同依赖进去:
JUnit:java测试事实上的标准,默认依赖版本是4.12(JUnit5和JUnit4差别比较大,集成方式有不同)。Spring Test & Spring Boot Test:Spring的测试支持。AssertJ:提供了流式的断言方式。Hamcrest:提供了丰富的matcher。Mockito:mock框架,可以按类型创建mock对象,可以根据方法参数指定特定的响应,也支持对于mock调用过程的断言。JSONassert:为JSON提供了断言功能。JsonPath:为JSON提供了XPATH功能。
下面举个简单的Demo
@SpringBootTest(classes = StudyApplication.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@RunWith(SpringRunner.class)
public class SpringBootTestDemo {
@Autowired
private TestRestTemplate testRestTemplate;
@Autowired
private SpringTestService springTestService;
private static final Logger LOGGER = LoggerFactory.getLogger(SpringBootTestDemo.class);
@Test
public void testGetStudentByRestTemplate() {
//通过TestRestTemplate直接调用接口,不用再依赖httpclient
ResponseEntity response = testRestTemplate.getForEntity("/test/students", Student.class);
LOGGER.info(response.getBody().toString());
}
@Test
public void testGetStudentByInjectService() {
//直接引入Service,调用Service里面的方法,TestRestTemplate我也不想要
Student student = springTestService.getStudent();
LOGGER.info(student.toString());
}
}
@RunWith是Junit4提供的注解,将Spring和Junit链接了起来。@SpringBootTest替代了spring-test中的@ContextConfiguration注解,目的是加载ApplicationContext,启动spring容器。使用@SpringBootTest时并没有像@ContextConfiguration一样显示指定locations或classes属性,原因在于@SpringBootTest注解会自动检索程序的配置文件,检索顺序是从当前包开始,逐级向上查找被@SpringBootApplication或@SpringBootConfiguration注解的类。使用@SpringBootTest后,Spring将加载所有被管理的bean,基本等同于启动了整个服务,此时便可以开始功能测试。
切片测试所谓切片测试,官网文档称为 “slice” of your application,实际上是对一些特定组件的称呼。这里的slice并非单独的类(毕竟普通类只需要基于JUnit的单元测试即可),而是介于单元测试和集成测试中间的范围。
slice是指一些在特定环境下才能执行的模块,比如MVC中的Controller、JDBC数据库访问、Redis客户端等,这些模块大多脱离特定环境后不能独立运行,假如spring没有为此提供测试支持,开发者只能启动完整服务对这些模块进行测试,这在一些复杂的系统中非常不方便,所以spring为这些模块提供了测试支持,使开发者有能力单独对这些模块进行测试。通过@*Test开启具体模块的测试支持,开启后spring仅加载相关的bean,无关内容不会被加载。
下面同样看个demo
@WebMvcTest(SpringTestController.class)
@RunWith(SpringRunner.class)
public class MockTest {
@Autowired
private MockMvc mockMvc;
private static final Logger LOGGER= LoggerFactory.getLogger(MockTest.class);
@Test
public void testMockStudent() throws Exception {
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.
get("/test/mockTest")).andReturn();
String content = result.getResponse().getContentAsString();
Student student = JSONObject.parseObject(content, Student.class);
LOGGER.info(student.toString());
}
}
使用@WebMvcTest和MockMvc搭配使用,可以在不启动web容器的情况下,对Controller进行测试(仅仅只是对controller进行简单的测试,如果Controller中依赖用@Autowired注入的service、dao等则不能这样测试),显然这种测试方式有很大的局限性。



