专栏首页软件测试那些事录制回放实现测试用例自由

录制回放实现测试用例自由

以后点点点就OK了

在本小节中,将介绍如何通过拦截HTTP请求,通过录制的方式形成测试用例

首先,我们来尝试一下如下的一个简单场景

1)调用MeterSphere的某个无参GET接口

2)录制该接口的请求和返回

3) 利用录制的结果再次执行前述接口调用

这个,就有点像“狗咬尾巴”了

用切面来抓取HTTP请求

首先来编写如下的一个切面

 package io.metersphere.aspect;
 
 import lombok.extern.slf4j.Slf4j;
 import org.aspectj.lang.JoinPoint;
 import org.aspectj.lang.annotation.*;
 import org.springframework.stereotype.Component;
 import org.springframework.web.context.request.RequestContextHolder;
 import org.springframework.web.context.request.ServletRequestAttributes;
 import javax.servlet.http.HttpServletRequest;
 import java.util.*;
 
 @Aspect
 @Component
 @Slf4j
 public class DBMapperIntercept {
     public static  List<MapperRecord> requests=new ArrayList<>();
     MapperRecord mapperRecord = new MapperRecord();
 
     @Pointcut("execution(public * io.metersphere..controller..*.*(..))")
     public void webLog(){}
 
     //指定切点前的处理方法
     @Before("webLog()")
     public void doBefore(JoinPoint joinPoint) throws Exception {
         //获取request对象
         ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
         if(attributes==null){
             log.info("请求为空");
             return;
         }
         HttpServletRequest request = attributes.getRequest();
         mapperRecord.setClassName(request.getRequestURI());
         mapperRecord.setMethodName(request.getMethod());
     }
 
     //指定切点前的处理方法
     @AfterReturning(pointcut = "webLog()",returning = "result")
     public void doAfterReturning(Object result) {
         mapperRecord.setReturning(result);
         requests.add(mapperRecord);
     }
 
 }

定义了 @Pointcut("execution(public * io.metersphere..controller...(..))") 这个切面,表示拦截所有controller的public方法调用。然后,在Spring Boot中,可以通过以下这个方法获取当前Request的属性,

ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

在获取到了当前HTTP请求的URI和调用类型(GET/POST)之后,我们将这些数据写入到一个record记录之中。

另外,我们还需要在@AfterReturning中拿到该HTTP接口的执行结果一并录入到记录中。

这样,我们拿到了一次HTTP服务接口测试所需的数据

1)服务的URL

2) 服务类型

3)预期结果

测试用例

来写一个测试用例验证一下

 package io.metersphere.controller;
 
 import com.alibaba.fastjson.JSON;
 import io.metersphere.TestApp;
 import io.metersphere.aspect.DBMapperIntercept;
 import io.metersphere.aspect.DBMock;
 import io.metersphere.aspect.MapperRecord;
 import io.metersphere.aspect.MockDBExtension;
 import net.javacrumbs.jsonunit.core.Option;
 import org.junit.jupiter.api.*;
 import org.junit.jupiter.api.extension.ExtendWith;
 import java.util.List;
 import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
 import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
 
 public class HelloControllerTest  extends TestApp {
     @BeforeEach
     public void testHelloController() throws Exception {
        String result= mockMvc.perform(get("/anonymous/hello"))
                 .andExpect(status().isOk())    //返回的状态是200
                 .andDo(print())         //打印出请求和相应的内容
                 .andReturn().getResponse().getContentAsString();   //将相应的数据转换为字符串
         ResultHolder resultHolder= JSON.parseObject(result,ResultHolder.class);
         assertThat(resultHolder.isSuccess()).isTrue();
     }
 
 
     @Test
     public void testGetWithoutParameters() throws Exception {
         List<MapperRecord> recordList=DBMapperIntercept.requests;
         assertThat(recordList).isNotEmpty();
         MapperRecord record=recordList.get(0);
         String result= runCase(record);
         assertThatJson(result).when(Option.IGNORING_EXTRA_FIELDS).isEqualTo(record.getReturning());
     }
 }

在这个用例中,首先在@BeforeEach中调用了一个无参的GET方法,并且断言该接口调用成功。在这个过程中,通过切面的请求拦截,将获取到的数据保存在了requests之中,用于在@Test中执行用例。

再来看一下执行方法

 public String runCase(MapperRecord record) throws Exception {
     String result = null;
     if (record.getMethodName().contains("GET")) {
         result = mockMvc.perform(get(record.getClassName()))
                 .andExpect(status().isOk())    //返回的状态是200
                 .andDo(print())         //打印出请求和相应的内容
                 .andReturn().getResponse().getContentAsString();   //将相应的数据转换为字符串
     }
     return result;
 }

目前这个方法非常简单,就是保证了上述请求能够顺利进行。

带参的POST请求

类似的,我们通过一个登录请求来展示如何拦截并实现带参POST请求的录制回放。

首先来看下这个请求

 @PostMapping(value = "/signin")
 public ResultHolder login(@RequestBody LoginRequest request) {
     SecurityUtils.getSubject().getSession().setAttribute("authenticate", UserSource.LOCAL.name());
     return userService.login(request);
 }

对于入参是这样定义的

 @Getter
 @Setter
 public class LoginRequest {
     private String username;
     private String password;
 }

因此,我们会有如下的一个简单的测试用例

 @Order(0)
 @Test
 public void loginSetup() throws Exception {
     LoginRequest loginRequest= new LoginRequest();
     loginRequest.setUsername("admin");
     loginRequest.setPassword("metersphere");
     doPost("/signin",JSON.toJSONString(loginRequest));
 }

这个用例会调用"/signin"接口,以"admin"用户成功登录。

请求拦截

在原先的案例中,我们在 public void doBefore(JoinPoint joinPoint) 方法中对GET请求进行了拦截并获取到了请求类型,根据请求类型为GET来进行相应的处理。

在这里,我们可以另外增加对POST类型的请求的处理。

 if (request.getMethod().equalsIgnoreCase(RequestMethod.POST.name())) {
     Object[] args = joinPoint.getArgs();
     mapperRecord.setArgs(args);

这里的Args主要对应的是存放在http.body中的请求体内容。

runcase的处理

在原先的runcase方法中额外再增加对POST类型的支持

 public String runCase(MapperRecord record) throws Exception {
     String result = null;
     String methodName = record.getMethodName();
     if (methodName.contains("GET")) {
         result = doGet(record.getClassName());  
     } else if (methodName.contains("POST")) {
         result = doPost(record.getClassName(), JSON.toJSONString(record.getArgs()[0]));
     }
     return result;
 }

这里对MockMVC的GET和POST请求的发送接收提供了统一的方法doGet和doPost。此外,还在原先GET方法处理的基础上,对POST方法也提供了处理。

这里提醒读者注意的是,由于在切面中抓取到的入参是一个Object [], 而实际上真正的POST请求的参数是一个登录对象。因此,我们需要从入参数组中取出该对象,并进行序列化,从而提供给doPost一个正确的请求入参。

以下是doPost的一个简单实现

     public String doPost(String url,String content ) throws Exception {
         return mockMvc.perform(post(url)
                 .content(content)
                 .contentType("application/json;charset=utf-8;")
                 .session(session)
         )
                 .andDo(print())         //打印出请求和相应的内容
                 .andExpect(status().isOk())    //返回的状态是200
                 .andReturn().getResponse().getContentAsString();
     }

测试用例-再次登录

在成功实现登录之后,我们再通过拦截录制得到的数据再次发起登录,有如下的用例,

     @Order(1)
     @Test
     public void testLoginRequest() throws Exception {
         List<MapperRecord> recordList= DBMapperIntercept.requests;
         assertThat(recordList).isNotEmpty();
         MapperRecord record=recordList.get(0);
         String result= runCase(record);
         assertThatJson(result).when(Option.IGNORING_EXTRA_FIELDS).isEqualTo(record.getReturning());
     }

这是执行该用例后,从服务端返回的结果。

可以看到status =200,请求的返回体中带有success=true的字样,说明admin用户成功登录了。

这说明POST请求也成功被拦截和录制回放了。

至此,简单的GET/POST请求均达成了目标。

本文分享自微信公众号 - 软件测试那些事(antony-not-available),作者:风月同天测试人

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-12-27

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • TDD案例-FizzBuzz-重构

    在之前的的TDD案例-FizzBuzz文章中,我们介绍了如何以TDD的方式,通过5个测试用例,来驱动我们实现了FizzBuzz。 本文将继续对FizzBuzz的...

    Antony
  • 在家隔离,不忘学习-FizzBuzz-TDD

    在之前的的TDD案例-FizzBuzz文章中,我们介绍了如何以TDD的方式,通过5个测试用例,来驱动我们实现了FizzBuzz。 本文将继续对FizzBuzz的...

    Antony
  • JsonUnit的正则匹配测试

    在最近的测试项目中,发现有个接口返回值的errorMsg中包含了时间信息,需要用到正则匹配的断言。

    Antony
  • 手把手教你开发微信公众号后台

    松哥原创的 Spring Boot 视频教程已经杀青,感兴趣的小伙伴戳这里-->Spring Boot+Vue+微人事视频教程

    江南一点雨
  • 网络爬虫 | Java 实现 AI人工智能技术 - 网络爬虫功能

    目前网络上充斥着越来越多的网页数据,包含海量的数据,但是很多时候,不管是出于对产品需求还是数据分析的需要,我们需要从这些网站上搜索一些相关的、有价值的数...

    码神联盟
  • Java第三方支付接入案例(支付宝)

    到蚂蚁金服注册开发者账号,注册地址:https://open.alipay.com,用你的 支付宝 账号扫码登录,完善个人信息,选择服务类型。

    朝雨忆轻尘
  • Spring学习-- SpEL表达式

    转载自 https://www.cnblogs.com/goodcheap/p/6490896.html

    allsmallpig
  • 从 http协议角度解析okhttp

    OkHttp 是 Square 公司开源的一款网络框架,封装了一个高性能的 http 请求库。

    开发者
  • 死磕YOLO系列,YOLOv2的自我修养

    YOLO 在当时是非常不错的算法,速度极快,但明显的缺陷就是精度问题特别是小尺寸目标检测问题上。

    Frank909
  • Laravel框架实现抢红包功能示例

    可以在信息界面自行选择 抢红包 或者 发红包 1.发红包,跳转到相应的发红包界面

    砸漏

扫码关注云+社区

领取腾讯云代金券