前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >接口自动化框架脚手架-参数化工具的实现

接口自动化框架脚手架-参数化工具的实现

作者头像
互联网金融打杂
发布2022-08-01 13:41:43
4830
发布2022-08-01 13:41:43
举报

作者:软件质量保障 知乎:https://www.zhihu.com/people/iloverain102

今天分享一个接口自动化框架开发中用到的脚手架工具。

题内话

众所周知,接口自动化用例最重要的是测试数据,而测试用例本质上依赖各种数据的组合。

做过接口自动化的朋友可以想一下,我们在使用postman或JMeter编写用例脚本的时候,比较费时间的就是接口间参数的传递了。

而通过现有工具实现接口间参数传递,只需要在下游接口中的“变量”设置{{}}或者${}即可,而如何替换的我们似乎并未考虑过。

在我们设计自己的框架时就不得不面对这个问题了。

针对微服务架构(本文只探讨Java栈)通常服务的实现并非基于http协议,接口的调用通过SPI(Service Provider Interface)接口实现,可以简单理解为一个标准服务,它就是一个Service,而非HTTP接口,它的请求报文就是一个Java对象。

大家都知道http协议接口的请求参数格式大多是json格式,但本文介绍的service服务的参数格式是对象。

因此,对于用例参数来说,service服务的自动化的实现要解决两个问题:

1.报文如何保存,以什么格式保存?

2.参数化如何实现?

对于测试来说,json格式数据比较容易组装,因为JSON结构比较容易接受,而且有很多JSON工具可以使用,我们只需要按照结构填充键值对即可,而且可以以JSON格式文件保存测试数据。

但是类对象就不一样了,类只是定义,对象是类的实例,需要在运行是new一个出来并给其赋值,因此我们没法以特定的格式or文件保存类对象。(当然也可以将其测试数据在测试代码里new一个对象出来并赋值,但是这太令人头大了

大家知道JSON和对象之间是可以互转的,想必大家都写过互转的代码,阿里巴巴也有一个开源工具fastJSON。 OK,这就解决了第一个问题,我们可以将类对象先转成json,以json文件格式保存本地作为报文的template。

而另一个问题怎么解决:如何参数化以及将JSON文件转为特定的对象?

其实解决这个问题也比较简单,我们需要参数化的变量定义成一个对象作为Input,而接口的请求参数—参数化变量其实就是业务不想干的数据,可以作为报文的模板。

那么参数化的过程其实就是将参数化对象的内容替换到报文模板中,然后将替换后的JSON转为Object即可。下图比较好理解啦。。

下面就撸代码实现下这个工具。

代码实践

这个工具的实现,需要依赖Apache的velocity包。Apache Velocity是一个基于Java的模板引擎,它提供了一个模板语言去引用由Java代码定义的对象。Velocity是Apache基金会旗下的一个开源软件项目,旨在确保Web应用程序在表示层和业务逻辑层之间的隔离。

下面是一些利用Velocity的常见应用场景:

  • Web应用程序:网页设计者创建HTML页面,并为动态信息预留占位符。页面再由VelocityViewServlet或任何支持Velocity的框架处理。
  • 源代码生成:Velocity可基于模板生成Java、SQL或PostScript源代码。大量的开源和商业软件包的开发就是这样利用Velocity。
  • 电子邮件自动生成:许多应用程序为了账户注册、密码提醒或自动寄送报表之需自动生成电子邮件。利用Velocity,电子邮件模板可以存储在一个文本文件,而不是直接嵌入到电子邮件生成器的Java代码中。
  • XML转化:Velocity提供一个Ant任务——Anakia。Anakia读取XML文件,利用Velocity模板转换成所需的文档格式。常见的应用是将某种格式的文档转换成的一个带样式的HTML文档。

好了,就贫嘴到这里,下面开始安静撸代码了。。。

项目结构

引入velocity包

代码语言:javascript
复制
  <dependency>
      <groupId>org.apache.velocity</groupId>
      <artifactId>velocity</artifactId>
      <version>1.7</version>
      </dependency>
    <dependency>
      <groupId>org.apache.velocity</groupId>
      <artifactId>velocity-tools</artifactId>
      <version>2.0</version>
    </dependency>

主要逻辑

代码语言:javascript
复制
import org.apache.velocity.VelocityContext;
/**
 * @author 软件质量保障
 * @version Json2Class.java, v 0.1 2022年06月24日 19:48 
 */
public class Json2Class {
​
    public static String evaluateString(String path, Object input) throws FileNotFoundException {
        InputStream is = new FileInputStream(path);
        Map<String, Object> context = new HashMap<>();
        Map<String, Object> paramMap = BeanMap.create(input);
        for (String key : paramMap.keySet()) {
            Object value = paramMap.get(key);
            if (value != null) {
                context.put(key, value);
            }
        }
        StringWriter writer = new StringWriter();
        String result;
        try {
            VelocityContext velocityContext = new VelocityContext(context);
            Velocity.evaluate(velocityContext, writer, "", is);
            result = writer.toString();
        } catch (Exception var8) {
            throw new TestException("velocity evaluate error[template=" + is + "]", var8);
        } finally {
            IOUtils.closeQuietly(writer);
        }
        return result;
    }
​
    public static <T> T reloadRequest(String path, Object input, Class<T> clazz) {
        try {
            String loadFile = evaluateString(path, input);
            T request = JSON.parseObject(loadFile, clazz);
            return request;
        } catch (FileNotFoundException e) {
            throw new RuntimeException("load request body failed",e);
        }
    }
}

测试代码

代码语言:javascript
复制
// 参数化变量对象
@Data
public class Input {
​
    private String paymentId;
    private String token;
    private Order order;
}
public class Order {
    private String amount;
    private String currency;
}
​
// 目标对象
@Data
public class Request {
​
    private String payId;
    private String paymentId;
    private String payType;
    private String token;
    private Order order;
​
}
// JSON模版
{
  "payId": "238938392",
  "token": "${token}",
  "paymentId": "${paymentId}",
  "payType": "CARD",
  "order": {
    "amount": "${order.amount}",
    "currency": "${order.currency}"
  }
}
​
// 客户端调用用例
public class client {
​
    public static void main(String[] args) throws FileNotFoundException {
        // 准备参数化数据
        Input input = new Input();
        Order order = new Order();
        order.setAmount("1000");
        order.setCurrency("CNY");
        input.setPaymentId("test0001");
        input.setToken("card_token_9999292929");
        input.setOrder(order);
​
        Request request =
                Json2Class.loadRequestBody(
                        "request.json", input, Request.class);
        System.out.println(request);
    }
}

输出结果

代码语言:javascript
复制
Request(payId=238938392, paymentId=test0001, payType=CARD, token=card_token_9999292929, order=Order(amount=1000, currency=CNY))

大功告成!!!

本案例比较简单,json没有循环嵌套多层,不管几层,本质是一样的,大家可以自行试试。

- END -


本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-07-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 题内话
  • 代码实践
  • 项目结构
  • 引入velocity包
  • 主要逻辑
  • 测试代码
  • 输出结果
相关产品与服务
腾讯云 BI
腾讯云 BI(Business Intelligence,BI)提供从数据源接入、数据建模到数据可视化分析全流程的BI能力,帮助经营者快速获取决策数据依据。系统采用敏捷自助式设计,使用者仅需通过简单拖拽即可完成原本复杂的报表开发过程,并支持报表的分享、推送等企业协作场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档