【开发篇】三、web下单元测试与mock数据

文章目录

  • 1、加载测试专用属性
  • 2、加载测试专用配置
  • 3、测试类中启动web环境
  • 4、发送虚拟请求
  • 5、匹配(断言)响应的执行状态
  • 6、匹配响应的结果
  • 7、匹配响应头
  • 8、业务层测试事务回滚
  • 9、UT数据设置随机数据

【开发篇】三、web下单元测试与mock数据_第1张图片

1、加载测试专用属性

写单元测试时,如果需要一个临时属性,但不能影响其他代码(即作用范围是当前UT类),有两种方式实现:

方式一:使用@SpringBootTest注解的properties属性

模块配置中,有一个test.prop属性:

test:
  prop: 9527

在UT中做临时修改:

@SpringBootTest(properties = {"test.prop=testValue1"})
public class PropertiesAndArgsTest {

    @Value("${test.prop}")    
    private String msg;    
    
    @Test    
    void testProperties(){        
   		System.out.println(msg);    
    }
}

以上这个写法:比多环境开发中的测试环境影响范围更小,仅对当前测试类有效。

方式二:使用args属性

在启动测试环境时可以通过args参数设置测试环境专用的传入参数:

@SpringBootTest(args = {"--test.prop=testValue2"})
public class PropertiesAndArgsTest {

    @Value("${test.prop}")    
    private String msg;    
    
    @Test    
    void testProperties(){        
   		System.out.println(msg);    
    }
}

以上这个args,就类比命令行参数启动、在启动类的args上赋值,–server.port=8080这个格式,第一种方式则类比properties文件,因此,当这两种方式同时设置一个属性时,args的方式优先级更高,生效。 以上这个加载测试临时属性,亮点是不影响其他UT或者其他模块代码,仅对当前测试类生效。

2、加载测试专用配置

源码中有一个Bean,定义了相关配置,如之前的MyBatisPlus的分页拦截器。那如何在UT里加一个配置Bean来辅助测试,且仅服务于这个UT类,如此,也不会引起配置冲突问题。先在test目录下定义一个配置Bean(肯定不能定义到com.src的实际源码里):

【开发篇】三、web下单元测试与mock数据_第2张图片

接下来使用@Import注解加载当前测试类专用的配置Bean

@SpringBootTest
@Import(MsgConfig.class)
public class ConfigurationTest {   
 
	@Autowired    
	private String msg;   
	
	@Test    
	void testConfiguration(){
	    System.out.println(msg);
	}
}

【开发篇】三、web下单元测试与mock数据_第3张图片

3、测试类中启动web环境

平时写的测试Mapper和Service层方法的UT,就是一个普通的Java程序,没有web环境,想在web环境下启动,可以使用@SpringBootTest注解的webEnvironment属性。

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class WebTest {    
	@Test    
		void testRandomPort () {    
	}
}

其中,属性取值可以为:使用源代码里定义的端口、随机端口、不启用web(默认)…

【开发篇】三、web下单元测试与mock数据_第4张图片
【开发篇】三、web下单元测试与mock数据_第5张图片

4、发送虚拟请求

在测试类中开启Web环境后,接下来在UT中直接测Controller层的接口:

  • 开启虚拟MVC调用
  • 注入MockMvc对象
  • 创建虚拟对象,传入接口路径
  • 执行请求
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
//开启虚拟MVC调用
@AutoConfigureMockMvc
public class WebTest {
    @Test
    //注入虚拟MVC调用对象
    public void testWeb(@Autowired MockMvc mvc) throws Exception {        
    	//创建虚拟请求,当前访问/books
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
        //执行请求        
        ResultActions action = mvc.perform(builder);    
    }
}

以上并不是一次正真的调用,而是虚拟出来了一套Web环境,在这个虚拟环境中发起了这次调用。记得加@AutoConfigureMockMvc开启需求Mvc调用,否则MockMvc这个Bean注入不上,至于注入写在形参里还是属性里,都行。

5、匹配(断言)响应的执行状态

在发送完虚拟请求调用接口后,接下来对请求响应的状态做一个匹配(断言):

  • 获取执行结果
  • 定义执行状态匹配器
  • 定义预期状态
  • 对比预期状态和真实状态
@Test
public void testSataus(@Autowired MockMvc mvc) throws Exception {    
	MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books/1");    
	ResultActions action = mvc.perform(builder);
	//匹配执行状态(是否预期值)    
	//定义执行状态匹配器    
	StatusResultMatchers status = MockMvcResultMatchers.status();    
	//定义预期执行状态    
	ResultMatcher ok = status.isOk();
	//使用本次真实执行结果与预期结果进行比对
	action.andExpect(ok);
}

写个不存在的路径,执行结果:

【开发篇】三、web下单元测试与mock数据_第6张图片

6、匹配响应的结果

和匹配状态一样,先看响应一个String的情况:

@Test
public void testSataus(@Autowired MockMvc mvc) throws Exception {    
	MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books/1");    
	ResultActions action = mvc.perform(builder);
	//匹配执行结果(是否预期值)    
	//定义执行结果匹配器    
	ContentResultMatchers content = MockMvcResultMatchers.content();    
	//定义预期执行结果    
	ResultMatcher result = content.string("test1");
	//使用本次真实执行结果与预期结果进行比对
	action.andExpect(result);
}

【开发篇】三、web下单元测试与mock数据_第7张图片

当预期结果是一个json时:

ResultMatcher result = content.json("{\"id\":1,\"name\":\"SpringBoot2\"}");

7、匹配响应头

大同小异,步骤一致:

@Test
public void testContentType(@Autowired MockMvc mvc) throws Exception {
    MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
    ResultActions action = mvc.perform(builder);
    //匹配器
    HeaderResultMatchers header = MockMvcResultMatchers.header();
    //预期结果
    ResultMatcher resultHeader = header.string("Content-Type", "application/json");
    action.andExpect(resultHeader);
}

正常写UT时,这三个连一起就行,比如,先获取执行状态匹配器,再定义预期,然后做匹配。再响应头、响应结果…

8、业务层测试事务回滚

在UT时,不让方法执行给数据库带来脏数据,可以加事务注解@Transactional,在有@SpringBootTest注解的情况下,Spring会识别到我们在做UT而回滚事务。

@SpringBootTest
@Transactional
public class DaoTest {

    @Autowired
    private BookService bookService;

	@Test
	void testSave(){
		bookservice.save(new Book());
	}

}

如果想在测试用例中提交事务,可以通过@Rollback注解,并设置value属性为false:

@SpringBootTest
@Transactional
@Rollback(false)  # 此时,会提交事务,即会影响数据库
public class DaoTest {

}

9、UT数据设置随机数据

测试用例数据通常采用随机值进行测试,使用SpringBoot提供的随机数为其赋值,举个例子:

testcast:
  book:
      id: ${random.int}           # 随机整数    
      id2: ${random.int(10)}      # 10以内随机数    
      type: ${random.int(10,20)}  # 10到20随机数    
      uuid: ${random.uuid}        # 随机uuid    
      name: ${random.value}       # 随机字符串,MD5字符串,32位    
      publishTime: ${random.long} # 随机整数(long范围)

注意:

  • ${random.int}表示随机整数
  • ${random.int(10)}表示10以内的随机数
  • ${random.int(10,20)}表示10到20的随机数
  • 其中表示范围的圆括号()可以是任意字符,例如[ ],!!均可

定义个实体类绑定下随机生成的数据:

【开发篇】三、web下单元测试与mock数据_第8张图片

测试类看下效果:

@Autowired
private Book book;

@Test
void testData(){
	System.out.println(book);
}

写懵了,刚开始竟然直接输出了一个new的Book对象,对象于Bean,前者不受Spring管控,也就是说Spring拿到随机数据也不能赋值给它,这个对象必须是Bean,所以改为了自动注入。

你可能感兴趣的:(SpringBoot,单元测试,log4j,springboot)