前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >录制回放实现测试用例自由

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

作者头像
Antony
修改2021-06-02 06:59:10
1.4K0
修改2021-06-02 06:59:10
举报

以后点点点就OK了

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

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

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

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

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

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

用切面来抓取HTTP请求

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

代码语言:javascript
复制
 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)预期结果

测试用例

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

代码语言:javascript
复制
 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中执行用例。

再来看一下执行方法

代码语言:javascript
复制
 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请求的录制回放。

首先来看下这个请求

代码语言:javascript
复制
 @PostMapping(value = "/signin")
 public ResultHolder login(@RequestBody LoginRequest request) {
     SecurityUtils.getSubject().getSession().setAttribute("authenticate", UserSource.LOCAL.name());
     return userService.login(request);
 }

对于入参是这样定义的

代码语言:javascript
复制
 @Getter
 @Setter
 public class LoginRequest {
     private String username;
     private String password;
 }

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

代码语言:javascript
复制
 @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类型的请求的处理。

代码语言:javascript
复制
 if (request.getMethod().equalsIgnoreCase(RequestMethod.POST.name())) {
     Object[] args = joinPoint.getArgs();
     mapperRecord.setArgs(args);

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

runcase的处理

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

代码语言:javascript
复制
 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的一个简单实现

代码语言:javascript
复制
     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();
     }

测试用例-再次登录

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

代码语言:javascript
复制
     @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请求均达成了目标。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-12-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 软件测试那些事 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 以后点点点就OK了
    • 用切面来抓取HTTP请求
      • 测试用例
        • 带参的POST请求
          • 请求拦截
            • runcase的处理
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档