专栏首页软件测试那些事MeterSphere系列04-通过Multi-Part接口新建用例

MeterSphere系列04-通过Multi-Part接口新建用例

我来给MeterSphere写测试用例04

继续我们的MeterSphere之旅。在本小节中,将介绍如何在MeterSphere中创建测试用例。

源码分析

参考之前文章中介绍的方式,可以找到新建测试用例所对应的后端controller和方法。示例代码如下,

 package io.metersphere.track.controller;
 
 
 @RequestMapping("/test/case")
 @RestController
 @RequiresRoles(value = {RoleConstants.ADMIN, RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER, RoleConstants.TEST_VIEWER, RoleConstants.ORG_ADMIN}, logical = Logical.OR)
 public class TestCaseController {
 
     @Resource
     TestCaseService testCaseService;
 
     @PostMapping(value = "/add", consumes = {"multipart/form-data"})
     @RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
     public void addTestCase(@RequestPart("request") EditTestCaseRequest request, @RequestPart(value = "file") List<MultipartFile> files) {
         testCaseService.save(request, files);
     }
 }

除了URL、POST、角色等等之前已经介绍过的内容之外,这个接口中出现了不同的内容那个。

  @PostMapping(value = "/add", consumes = {"multipart/form-data"})

首先是接口类型的注解中,首次出现了consumes = {"multipart/form-data"}这样的属性。这种接口主要是用于上传文件与等服务器交互的操作 。通过在 MeterSpher 在线体验环境: https://demo.metersphere.com/ (账号:demo 密码:P@ssw0rd123..)上的试验操作,可以发现MeterSphere的确允许在新建用例的同时上传用于进一步描述用例的附件,且可以上传多个。如下图所示,

Multipart/form-data是基于Post的请求,不过与普通Post的请求体不同的是它的构造方式 。普通的Post的请求体是简单的name=value组成的列表 , 而Multipart/form-data则是添加了分隔符等内容的构造体。因此,需要进一步来观察这个接口来了解具体的组成。

在接口的入参中,有如下的两个注解,分别表示一种为字符串类型参数,另一种为文件类型参数。

 @RequestPart("request") EditTestCaseRequest request, @RequestPart(value = "file")

这说明, 这个Multipart的结构体有两部分组成

  • request , 这是一个key=request,value是一个序列化之后的EditTestCaseRequest实例的二进制
  • file, 表示从客户端上传的文件

所以,这个接口的请求体一部分是和普通post一样的JSON对象,用于表述测试用例自身的属性,另外一部分则是用于上传这个用例的附件。

测试用例

以下用例用于在MeterSphere中新建一个测试用例。

   @Order(3)
     @Test
     @DisplayName("03用例-新建用例")
     public void addTestCase() throws Exception {
     testCase.setName("case-"+getRandom());
     testCase.setMaintainer("admin");
     testCase.setProjectId(project.getId());
     testCase.setNodeId(testCaseNode.getId());
     testCase.setMethod("manual");
     testCase.setPriority("P3");
     testCase.setType("functional");
     testCase.setNodePath("/"+testCaseNode.getName());
      String result=   doPostMultipartFormData("/test/case/add",getTestCaseRequest(getEditTestCaseRequest(testCase)),null);
         assertThat(result).contains("true");
         result=doGet("/test/case/recent/1");
         List<TestCase> testCases= JSON.parseArray(String.valueOf(JSON.parseObject(result,ResultHolder.class).getData()),TestCase.class);
         assertThat(testCases).isNotEmpty();
         testCase=testCases.get(0);
 
     }

上述只是一个简化的示例,主要是展示了如何将一个TestCase实例序列化后塞进Multi-Part类型的请求体中,并通过Post方式发送给后端服务接口。由于附件只是一个测试用例的可选项,请注意此案例并没有上传附件。

Multi-Part接口封装

从测试用例中可以看到,上述需求可以通过封装一个类似doPost的方法来实现,在这里作为示例的是一个doPostMultipartFormData的方法。它接受URL,以及两个MockMultipartFile类型的入参。

     public String doPostMultipartFormData(String url, MockMultipartFile file, MockMultipartFile anotherFile) throws Exception {
         return mockMvc.perform(
                 MockMvcRequestBuilders.multipart(url)
                       .file(file)
                      //   .file(anotherFile)
                 .contentType("multipart/form-data")
                 .session(session)
         )
                 .andDo(print())         //打印出请求和相应的内容
                 .andExpect(status().isOk())    //返回的状态是200
                 .andReturn().getResponse().getContentAsString();
     }

MockMvc的MockMvcRequestBuilders提供了专门的multipart方法来构建这样的请求。按照之前提到的由于是简化用例,并不提供附件,因此接收附件的第二个MockMultipartFile类型的入参并没有被使用。

接下来问题则是,如何将普通的TestCase序列化之后的字符串转换成这个接口能够接受的MockMultipartFile类型,并命名为"request"。

因此,有如下的一个私有方法用来转换

     private MockMultipartFile getTestCaseRequest(EditTestCaseRequest request){
         return new MockMultipartFile("request", "", "application/json", JSON.toJSONString(request).getBytes());
     }

由于MockMultipartFile只接受二进制的内容,因此需要将TestCase序列化之后再转换成二进制。这里还需要指出一下,"application/json"是必须指定的,不然在后端接口进行解析时会导致TestCase会反序列化失败。

执行结果

以下是通过

 mockMvc.andDo(print())

打印出的请求和响应。

 MockHttpServletRequest:
       HTTP Method = POST
       Request URI = /test/case/add
        Parameters = {}
           Headers = [Content-Type:"multipart/form-data"]
              Body = <no character encoding set>
     Session Attrs = {}
 
 Handler:
              Type = io.metersphere.track.controller.TestCaseController
            Method = io.metersphere.track.controller.TestCaseController#addTestCase(EditTestCaseRequest, List)
 
 Async:
     Async started = false
      Async result = null
 
 Resolved Exception:
              Type = null
 
 ModelAndView:
         View name = null
              View = null
             Model = null
 
 FlashMap:
        Attributes = null
 
 MockHttpServletResponse:
            Status = 200
     Error message = null
           Headers = [Content-Type:"application/json"]
      Content type = application/json
              Body = {"success":true,"message":null,"data":null}
     Forwarded URL = null
    Redirected URL = null
           Cookies = []

可以看到响应中"success":true,表示用例被成功地创建了。比较遗憾的是响应结构体中的data部分并没有返回类似TestCase id之类的信息。为了能够让整个旅程能继续下去,需要得到刚才新建的测试用例ID。因此需要额外通过查询接口来获取到最近一个用例,也就是刚才新建的用例。

 result=doGet("/test/case/recent/1");
 List<TestCase> testCases= JSON.parseArray(String.valueOf(JSON.parseObject(result,ResultHolder.class).getData()),TestCase.class);
 assertThat(testCases).isNotEmpty();
 testCase=testCases.get(0);

此处,建议MeterSphere团队能够优化一下这个接口。

小节一下

”multipart/form-data"类型的请求是基于Post的一种特殊请求,一般用于文件上传,同时支持传输额外的数据。

MockMvc的MockMvcRequestBuilders提供了专门的multipart方法来支持”multipart/form-data"类型的请求。

如果@RequestPart("request")是一个对象,则在构建MockMultipartFile实例时,contentType需要指定为"application/json"。

MockMVC提供了MockMvcResultHandlers.print方法来打印请求和应答。

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

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

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

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

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 我来给MeterSphere写测试用例06

    在本文中,将介绍如何通过接口的方式完成测试评审任务的新建、并将之前新建的测试用例纳入该测试评审任务,并最终将该用例标注为评审通过。

    Antony
  • @SpringBootTest和@WebMvcTest并用?

    这两个注解分别使用了不同Bootstrap来启动应用的上下文。 @BootstrapWith(WebMvcTestContextBootstrapper.cla...

    Antony
  • 临时工 - PowerMock系列4

    在测试过程中,发现我们的开发同学喜欢在方法中临时new 出一些类来完成某项工作。由于局部变量用完立即销毁了,使用起来也就非常灵活和随意了。 但这样就对单元测试造...

    Antony
  • bootstrap treeview 增删改的正确姿势

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hotqin888/article/det...

    hotqin888
  • 深入浅出机器学习中的决策树(一)

    以下材料最好用Jupyter notebook 阅读,如果您克隆course repository,可以使用Jupyter在本地复制。

    银河1号
  • 单数据和批量数据的删除操作

    通常对某条数据的删除和某一批数据的删除分别采用两个成员方法。这样太累赘了一些,为了使用批量删除的成员方法,就需要构造单数据的结构。这里以ID为数组作为例子

    世纪访客
  • 计算MySQL表碎片的SQL整理

    当然整理的过程不光是知识梳理的过程,也是转化为实践场景的一个过程,通过这样一个体系,对于整个MySQL对象生命周期管理有了较为深入的认识,这里我来抛砖引玉,来作...

    jeanron100
  • 机器学习笔记之决策树分类Decision Tree

    决策树(decision tree)是一种依托于策略抉择而建立起来的树。机器学习中,决策树是一个预测模型;他代表的是对象属性与对象值之间的一种映射关系。 树中每...

    Jetpropelledsnake21
  • update关联表

    斯文的程序
  • 【盘点】今年最酷的10项智能家居黑科技

    1.悬浮产品 科技的发展让很多电影情节变成了现实,像把物体悬浮在半空中的“特效”,在家里也能轻松实现。这些悬浮产品把科技与设计结合,让家居生活变得...

    钱塘数据

扫码关注云+社区

领取腾讯云代金券