zoukankan      html  css  js  c++  java
  • SpringBoot2 集成测试组件,七种测试手段对比

    一、背景描述

    在版本开发中,时间段大致的划分为:需求,开发,测试;

    • 需求阶段:理解需求做好接口设计;
    • 开发阶段:完成功能开发和对接;
    • 测试上线:自测,提测,修复,上线;

    实际上开发阶段两个核心的工作,开发和流程自测,自测的根本目的是为自己提前解决可能出现的问题;如果缺少自测和提测两个关键步骤,那么问题就会被传递给更多的用户,产生更多的资源消耗;

    自测是于开发而言,提测是对专业的测试人员而言,如果尽可能在自测阶段就发现问题,并解决问题,那么一个问题就不会影响到团队协作上的更多人员,如果一个简单的问题上升到团队协作层面,很可能会导致问题本身被放大

    工欲善其事必先利其器,开发如果要做好自测流程,学会使用工具提高效率是十分关键的,自测的关键在于发现问题和解决问题,所以选择好用和高效的工具可以极大的降低自测的时间消耗。

    下面围绕几个自己开发过程中常用的测试工具和手段,做简单的总结,不在于对比方式的好坏,存在即合理,在不同场景中对合理手段的选择,快速解决问题才是根本目的

    二、PostMan工具

    PostMan很常用的接口测试工具,开发过程中快速测试接口,功能强大并且简单方便,不但可以单个接口测试,也可以对接口分块管理批量运行:

    整体来说工具比较好用,适应于开发阶段的接口快速测试,或者在解决问题的过程中单个接口的测试,同时对测试参数有存储和记忆能力,这也是受欢迎的一大原因。

    但是该工具不适应于复杂的流程化测试,例如需要根据上次接口的响应报文做分别处理,或者下次请求需要填充某个接口响应的数据。

    三、Swagger文档

    Swagger管理接口文档,是当下服务中很常用的组件,通过对接口和对象的简单注释,快速生成接口描述信息,并且可以对接口发送请求,协助调试,该文档在前后端联调中极大的提高效率。

    接口文档的管理本身是一件麻烦事,接口通常会根据业务不断的调整,如果单独维护一份接口文档,需要付出很多时间成本,并且容易出问题,利用swagger就可以避免这个问题。

    借助swagger注解标记对象

    @TableName("jt_activity")
    @ApiModel(value="活动PO对象", description="活动信息表【jt_activity】")
    public class Activity {
    
        @ApiModelProperty(value = "主键ID")
        @TableId(type = IdType.AUTO)
        private Integer id;
    
        @ApiModelProperty(value = "活动主题")
        private String activityTitle;
    
        @ApiModelProperty(value = "联系号码")
        private String contactPhone;
    
        @ApiModelProperty(value = "1线上、2线下")
        private Integer isOnline;
    
        @ApiModelProperty(value = "举办地址")
        private String address;
    
        @ApiModelProperty(value = "主办单位")
        private String organizer;
    
        @ApiModelProperty(value = "创建时间")
        private Date createTime;
    }
    

    借助swagger注解标记接口

    @Api(tags = "活动主体接口")
    @RestController
    public class ActivityWeb {
    
        @Resource
        private ActivityService activityService ;
    
        @ApiOperation("新增活动")
        @PostMapping("/activity")
        public Integer save (@RequestBody Activity activity){
            activityService.save(activity) ;
            return activity.getId() ;
        }
    
        @ApiOperation("主键查询")
        @GetMapping("/activity/{id}")
        public Activity getById (@PathVariable("id") Integer id){
            return activityService.getById(id) ;
        }
    
        @ApiOperation("修改活动")
        @PutMapping("/activity")
        public Boolean updateById (@RequestBody Activity activity){
            return activityService.updateById(activity) ;
        }
    }
    

    通常来说,基于swagger注解标记接口类和方法上的入参和关键返参对象即可,这样可以避免再单独维护接口文档。

    Swagger接口文档在开发的过程中更多是扮演文档的角色,真正使用swagger去调试的接口也常是一些增删改查的简单接口,这个工具也同样不适应于复杂流程的测试。

    四、TestRestTemplate类

    SpringBoot测试包中集成的测试API,需要依赖测试包,可以访问控制层接口,非常方便的完成交互过程:

    Jar包依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    

    使用案例

    @RunWith(SpringRunner.class)
    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    public class ActivityTest01 {
        protected static Logger logger = LoggerFactory.getLogger(ActivityTest01.class) ;
    
        @Resource
        private TestRestTemplate restTemplate;
    
        private Activity activity = null ;
        @Before
        public void before (){
            activity = restTemplate.getForObject("/activity/{id}", Activity.class,1);
            logger.info("
    "+JSONUtil.toJsonPrettyStr(activity));
        }
        @Test
        public void updateById (){
            if (activity != null){
                activity.setCreateTime(new Date());
                activity.setOrganizer("One商家");
                restTemplate.put("/activity",activity);
            }
        }
        @After
        public void after (){
            activity = restTemplate.getForObject("/activity/{id}", Activity.class,1);
            logger.info("
    "+JSONUtil.toJsonPrettyStr(activity));
            activity = null ;
        }
    }
    

    在TestRestTemplate源码中可以发现,基于RestTemplate做封装,很多功能的实现都是调用RestTemplate方法。

    用写代码的方式去实现接口测试,灵活度非常高,可以根据流程做定制开发,很适应于中等复杂的场景测试,这里为什么这样描述,下面对比Http请求再细说。

    五、Http请求模式

    通过模拟接口的Http请求实现的方式,目前来说个人感觉灵活的最高的方式,先看简单的案例:

    @RunWith(SpringRunner.class)
    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
    public class ActivityTest03 {
        protected static Logger logger = LoggerFactory.getLogger(ActivityTest03.class) ;
        protected static String REQ_URL = "服务地址+端口";
    
        @Test
        public void testHttp (){
            // 查询
            String getRes = HttpUtil.get(REQ_URL+"activity/1");
            logger.info("
     {} ",JSONUtil.toJsonPrettyStr(getRes));
            Activity activity = JSONUtil.toBean(getRes, Activity.class) ;
            // 新增
            activity.setId(null);
            activity.setOrganizer("Http商家");
            String saveRes = HttpUtil.post(REQ_URL+"/activity",JSONUtil.toJsonStr(activity));
            logger.info("
     {} ",saveRes);
            // 更新
            activity.setId(Integer.parseInt(saveRes));
            activity.setOrganizer("Put商家");
            String putRes = HttpRequest.put(REQ_URL+"/activity")
                    .body(JSONUtil.toJsonStr(activity)).execute().body();
            logger.info("
     {} ",putRes);
        }
    }
    

    这种方式对于复杂的业务流程来说非常好用,当然这里不排除个人习惯,在测试复杂流程的时候,一个简单方案:

    • 用户信息:模拟http中token数据;
    • 业务流程:通过数据获取包装参数模型;
    • 独立服务管理,模拟并发场景;
    • 根据执行过程生成分析数据结果;

    对于复杂业务流程的测试,每个节点的模拟都具有一定的难度,通常在完整的流程中涉及到的服务和库表都是多个,并且请求链路复杂,基于一个灵活的自动化流程,去测试完整的链路,可以对效率有极大的提升。

    六、Service层测试

    针对服务层的测试手段,其本意在于业务实现的逻辑测试:

    @RunWith(SpringRunner.class)
    @SpringBootTest(classes = Application.class)
    public class ActivityTest04 {
        protected static Logger logger = LoggerFactory.getLogger(ActivityTest04.class) ;
    
        @Autowired
        private ActivityService activityService ;
    
        @Test
        public void testService (){
            // 查询
            Activity activity = activityService.getById(1) ;
            // 新增
            activity.setId(null);
            activityService.save(activity) ;
            // 修改
            activity.setOrganizer("Ser商家");
            activityService.updateById(activity) ;
            // 删除
            activityService.removeById(activity.getId()) ;
        }
    }
    

    该测试在实际的开发过程也并不常用,偶尔在于某个业务方法实现难度很大,用来针对性测试。

    七、MockMvc方式

    MockMvc同样是SpringBoot集成测试包提供的测试方式,通过对象的模拟,验证接口是否符合预期:

    @AutoConfigureMockMvc
    @RunWith(SpringRunner.class)
    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
    public class ActivityTest02 {
        protected static Logger logger = LoggerFactory.getLogger(ActivityTest02.class) ;
        @Resource
        private MockMvc mockMvc ;
    
        private Activity activity = null ;
    
        @Before
        public void before () throws Exception {
            ResultActions resultAction = mockMvc.perform(MockMvcRequestBuilders.get("/activity/{id}",1)) ;
            MvcResult mvcResult = resultAction.andReturn() ;
            String result = mvcResult.getResponse().getContentAsString();
            activity = JSONUtil.toBean(result,Activity.class) ;
        }
    
        @Test
        public void updateById () throws Exception {
            activity.setId(null);
            activity.setCreateTime(new Date());
            activity.setOrganizer("One商家");
            ResultActions resultAction = mockMvc.perform(MockMvcRequestBuilders.post("/activity")
                                                .contentType(MediaType.APPLICATION_JSON)
                                                .content(JSONUtil.toJsonStr(activity))) ;
            MvcResult mvcResult = resultAction.andReturn() ;
            String result = mvcResult.getResponse().getContentAsString();
            activity.setId(Integer.parseInt(result));
            logger.info("result : {} ",result);
        }
    
        @After
        public void after () throws Exception {
            activity.setCreateTime(new Date());
            activity.setOrganizer("Update商家");
            ResultActions resultAction = mockMvc.perform(MockMvcRequestBuilders.put("/activity")
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(JSONUtil.toJsonStr(activity))) ;
            MvcResult mvcResult = resultAction.andReturn() ;
            String result = mvcResult.getResponse().getContentAsString();
            logger.info("result : {} ",result);
        }
    }
    

    对于这种Mock类型的测试,非常专业,通常个人使用极少,暂时没有Get到其精髓思想。

    八、Mockito测试

    Mock属于非常专业和标准的测试手段,需要依赖powermock包:

    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-core</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-api-mockito2</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-module-junit4</artifactId>
        <scope>test</scope>
    </dependency>
    

    简单使用案例:

    @RunWith(PowerMockRunner.class)
    @SpringBootTest
    public class ActivityTest05 {
    
        @Test
        public void testMock (){
            Set mockSet = PowerMockito.mock(Set.class);
            PowerMockito.when(mockSet.size()).thenReturn(10);
            int actual = mockSet.size();
            int expected = 15 ;
            Assert.assertEquals("返回值不符合预期",expected, actual);
        }
    
        @Test
        public void testTitle (){
            String expectTitle = "Mock主题" ;
            Activity activity = PowerMockito.mock(Activity.class);
            PowerMockito.when(activity.getMockTitle()).thenReturn(expectTitle);
            String actualTitle = activity.getMockTitle();
            Assert.assertNotEquals("主题相符", expectTitle, actualTitle);
        }
    }
    

    可以通过Mock方式,快速模拟出复杂的对象结构,以便构建测试方法,由于使用很少,同样个人暂时没Get到点。

    九、源代码地址

    GitHub·地址
    https://github.com/cicadasmile/middle-ware-parent
    GitEE·地址
    https://gitee.com/cicadasmile/middle-ware-parent
    

    阅读标签

    Java基础】【设计模式】【结构与算法】【Linux系统】【数据库

    分布式架构】【微服务】【大数据组件】【SpringBoot进阶】【Spring&Boot基础

    数据分析】【技术导图】【 职场

  • 相关阅读:
    java
    图解MotionEvent中getRawX、getRawY与getX、getY以及View中的getScrollX、getScrollY
    ListView分栏--制作分栏音乐列表
    Android中Touch事件的传递机制
    Android进程间通信之使用AIDL
    Android进程间通信之使用Messenger
    Android Mms之短信接收流程--从Framework到App
    Android组件的使用:ListView、GridView
    Android组件的使用:AutoCompleteTextView、Spinner
    Android组件的使用:DatePicker,TimePicker
  • 原文地址:https://www.cnblogs.com/cicada-smile/p/14830938.html
Copyright © 2011-2022 走看看