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



    请添加图片描述

    1、加载测试专用属性

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

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

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

    test:
      prop: 9527
    
    • 1
    • 2

    UT中做临时修改:

    @SpringBootTest(properties = {"test.prop=testValue1"})
    public class PropertiesAndArgsTest {
    
        @Value("${test.prop}")    
        private String msg;    
        
        @Test    
        void testProperties(){        
       		System.out.println(msg);    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

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

    方式二:使用args属性

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

    @SpringBootTest(args = {"--test.prop=testValue2"})
    public class PropertiesAndArgsTest {
    
        @Value("${test.prop}")    
        private String msg;    
        
        @Test    
        void testProperties(){        
       		System.out.println(msg);    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

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

    2、加载测试专用配置

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

    在这里插入图片描述

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

    @SpringBootTest
    @Import(MsgConfig.class)
    public class ConfigurationTest {   
     
    	@Autowired    
    	private String msg;   
    	
    	@Test    
    	void testConfiguration(){
    	    System.out.println(msg);
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述

    3、测试类中启动web环境

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

    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    public class WebTest {    
    	@Test    
    		void testRandomPort () {    
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

    在这里插入图片描述
    在这里插入图片描述

    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);    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    以上并不是一次正真的调用,而是虚拟出来了一套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);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

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

    在这里插入图片描述

    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);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述

    当预期结果是一个json时:

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

    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);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

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

    8、业务层测试事务回滚

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

    @SpringBootTest
    @Transactional
    public class DaoTest {
    
        @Autowired
        private BookService bookService;
    
    	@Test
    	void testSave(){
    		bookservice.save(new Book());
    	}
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

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

    @SpringBootTest
    @Transactional
    @Rollback(false)  # 此时,会提交事务,即会影响数据库
    public class DaoTest {
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    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范围)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    注意:

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

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

    在这里插入图片描述

    测试类看下效果:

    @Autowired
    private Book book;
    
    @Test
    void testData(){
    	System.out.println(book);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

  • 相关阅读:
    极简实用PyTorch记录——如何读取图片、改变大小、转化为numpy数组、转化为tensor
    XXL-job-oracle 版本
    用Navicat备份Mysql演示系统数据库的时候出:Too Many Connections
    Python + Django4 搭建个人博客(七): Admin后台管理系统
    【代码随想录】栈与队列专栏(java版本)
    AI DevOps | ChatGPT 与研发效能、效率提升(中)
    计算机毕业设计安卓App毕设项目之ssmAndroid安全网购平台
    LCD1602
    BEM命名法
    金融期货账号怎么开?
  • 原文地址:https://blog.csdn.net/llg___/article/details/133170857