接口测试用例具有可复用性,便于创建复杂测试场景
  很多情况下,系统的接口都是有业务逻辑关联的。例如,要请求调用登录接口,需要先请求获取验证码的接口,然后在登录请求中带上获取到的验证码;而要请求数据查询的接口,又要在请求参数中包含登录接口返回的session值。这个时候,我们如果针对每一个要测的业务逻辑,都单独描述要请求的接口,那么会造成大量的重复描述,测试用例的维护也十分臃肿。
  比较好的做法是,将每一个接口调用单独封装为一条测试用例,然后在描述业务测试场景时,选择对应的接口,按照顺序拼接为业务场景测试用例,像搭积木一般。如果你之前读过 AppiumBooster 的介绍,应该还会联想到,我们可以将常用的功能组成模块用例集,然后可以在更高的层面对模块用例集进行组装,实现更复杂的测试场景。
  不过,这里有一个非常关键的问题需要解决,是如何在接口测试用例之前传参的问题。其实实现起来也不复杂,我们可以在接口请求响应结果中指定一个变量名,然后将接口返回关键值提取出来后赋值给那个变量;然后在其它接口请求参数中,传入这个 ${变量名} 即可。
  测试执行方式简单灵活,支持单接口调用测试、批量接口调用测试、定时任务执行测试
  通过背景中的例子可以看出,需要使用接口测试工具的场景很多,除了定时地对所有接口进行自动化测试检测外,很多时候在手工测试的时候也需要采用接口测试工具进行辅助,也是 半手工+半自动化 的模式。
  而业务测试人员在使用测试工具的时候,遇到的大问题在于除了需要关注业务功能本身,还需要花费很多时间去处理技术实现细节上的东西,例如签名校验这类情况,而且往往后者在重复操作中占用的时间更多。
  这个问题的确是没法避免的,毕竟不同系统的接口千差万别,不可能存在一款工具可以自动处理所有情况。但是我们可以尝试将接口的技术细节实现和业务参数进行拆分,让业务测试人员只需要关注业务参数部分。
  具体地,我们可以针对每一个接口配置一个模板,将其中与业务功能无关的参数以及技术细节封装起来,例如签名校验、时间戳、随机值等,而与业务功能相关的参数配置为可传参的模式。
  这样做的好处在于,与业务功能无关的参数以及技术细节我们只需要封装配置一次,而且这个工作可以由开发人员或者测试开发人员来实现,减轻业务测试人员的压力;接口模板配置好后,测试人员只需要关注与业务相关的参数即可,结合业务测试用例,可以在接口模板的基础上很方便地配置生成多个接口测试用例。
  测试结果统计报告简洁清晰,附带详尽日志记录,包括接口请求耗时、请求响应数据等
  测试结果统计报告,应该遵循简洁而不简单的原则。“简洁”,是因为大多数时候我们只需要在短的时间内判断所有接口是否运行正常即可。而“不简单”,是因为当存在执行失败的测试用例时,我们期望能获得接口测试时尽可能详细的数据,包括测试时间、请求参数、响应内容、接口响应耗时等。
  之前在读 locust 源码时,其对 HTTP 客户端 的封装方式给我留下了深刻的印象。它采用的做法是,继承 requests.Session 类,在子类 HttpSession 中重写覆盖了 request 方法,然后在 request 方法中对 requests.Session.request 进行了一层封装。
 request_meta = {}
  # set up pre_request hook for attaching meta data to the request object
  request_meta["method"] = method
  request_meta["start_time"] = time.time()
  response = self._send_request_safe_mode(method, url, **kwargs)
  # record the consumed time
  request_meta["response_time"] = int((time.time() - request_meta["start_time"]) * 1000)
  request_meta["content_size"] = int(response.headers.get("content-length") or 0)
  而 HttpLocust 的每一个虚拟用户(client)都是一个 HttpSession 实例,这样每次在执行 HTTP 请求的时候,既可充分利用 Requests 库的强大功能,同时也能将请求的响应时间、响应体大小等原始性能数据进行保存,实现可谓十分优雅。
  受到该处启发,要保存接口的详细请求响应数据也可采用同样的方式。例如,要保存 Response 的 Headers 、 Body 只需要增加如下两行代码:
request_meta["response_headers"] = response.headers
request_meta["response_content"] = response.content
  身兼多职,同时实现接口管理、接口自动化测试、接口性能测试(结合Locust)
  其实像接口性能测试这样的需求,不应该算到接口自动化测试框架的职责范围之内。但是在实际项目中需求是这样,又要做接口自动化测试,又要做接口性能测试,而且还不想同时维护两套代码。
  多亏有了 locust 性能测试框架,接口自动化和性能测试脚本还真能合二为一。
  前面也讲了, HttpLocust 的每一个虚拟用户(client)都是一个 HttpSession 实例,而 HttpSession 又继承自 requests.Session 类,所以 HttpLocust 的每一个虚拟用户(client)也是 requests.Session 类的实例。
  同样的,我们在用 Requests 库做接口测试时,请求客户端其实也是 requests.Session 类的实例,只是我们通常用的是 requests 的简化用法。
  以下两种用法是等价的。
 resp = requests.get('http://debugtalk.com')
  # 等价于
  client = requests.Session()
  resp = client.get('http://debugtalk.com')
  有了这一层关系以后,要在接口自动化测试和性能测试之间切换很容易了。在接口测试框架内,可以通过如下方式初始化 HTTP 客户端。
def __init__(self, origin, kwargs, http_client_session=None):
     self.http_client_session = http_client_session or requests.Session()
  默认情况下, http_client_session 是 requests.Session 的实例,用于进行接口测试;当需要进行性能测试时,只需要传入 locust 的 HttpSession 实例即可。
  具有可扩展性,便于扩展实现Web平台化
  当要将测试平台推广至更广阔的用户群体(例如产品经理、运营人员)时,对框架实现Web化在所难免了。在Web平台上查看接口测试用例运行情况、对接口模块进行配置、对接口测试用例进行管理,的确会便捷很多。
  不过对于接口测试框架来说, Web平台 只能算作锦上添花的功能。我们在初期可以优先实现命令行(CLI)调用方式,规范好数据存储结构,后期再结合Web框架(如Flask)增加实现Web平台功能。
  写在后面
  以上便是我对 ApiTestEngine 特性的详细介绍,也算是我个人对接口自动化测试 佳工程实践 的理念阐述。