专栏首页测试开发技术一套测试用例如何实现支持多个环境运行
原创

一套测试用例如何实现支持多个环境运行

点击右侧「测试开发技术」,选择“关注”,优质文章,第一时间送达!

公众号:测试开发技术(mikezhou_talk)

一款需要正式对外发布的产品,通常都需要经历一个较完整的测试验证过程,在整个产品质量验证阶段,一般会经历几类测试环境的验证:从产品集成阶段的测试环境->验收阶段的预发布环境->正式发布回归的生产环境

由于不同环境之间或多或少存在一些差异性,为了能将这些环境差异性导致的问题充分暴露出来,测试人员需要在这些不同的环境中都要进行必要的测试验证。

接口自动化测试作为质量保障的一种手段,除了用在测试阶段,也需要用在预发布环境和生产环境。

很多时候,为了能让测试用例运行在多套环境中,不得不维护多套测试脚本、测试用例。这种方式虽然可行,但会造成大量的测试用例、测试脚本冗余,以及巨大的后期维护工作量。

那么有没有一种方式或者说实现策略,可以实现一套接口测试用例可按照特定测试需求运行在多套环境中呢?答案是肯定的

接下来,就带着大家,分别从测试框架语言实现两个层面介绍如何实现一套测试自动化用例脚本运行在多个环境下(属于自动化测试实施高阶技巧)。

相信很多读者能感受到一个明显现象,公司规模越大,对各类环境的定义也会更加清晰、明确,环境种类也会进一步的细分。这么多环境的加持下,对自动化测试实施过程提出了一个挑战或者说是需求:自动化用例应当支持在不同环境里执行,并且对用例逻辑层透明无感。

为了实现诸如此,有些人,采取了较为”傻瓜式“的方式,拿下面这段代码为例。

def test_login(self):
    if env = "dev":
        requests.post("https://dev.xxx.com/login", data={"username":"dev", "password":"123456"})
        do_something()
    elif env = "test":
         requests.post("https://test.xxx.com/login", data={"username":"test", "password":"123456"})
         do_something()
    elif env = "pre":
        requests.post("https://pre.xxx.com/login", data={"username":"pre", "password":"123456"})
        do_something()
    else:
        requests.post("https://www.xxx.com/login", data={"username":"superadmin", "password":"123456"})
        do_something()

看完上述代码写法,有没有似曾相识的同学,如果有的话,很不幸地告诉你,你采取了最不为推荐的方法。上述示例还仅仅只是一条用例,如果自动化测试用例体量大(实际都不会小),自动化测试用例脚本维护代码重复量带来的灾难性可想而知。

仔细分析一下,要实现一套测试用例在多环境下执行,要解决哪些问题:

  • 不同环境的服务入口地址不同,一般还会有http/https的差别
  • 不同环境需要使用不同的测试数据
  • 一些中间件,比如数据库、消息队列、缓存服务的访问地址、账号、配置有差别
  • 不同环境的第三方回调地址有差别
  • 不同环境的配置需要整体切换,不能出现在测试环境里用了生产环境的数据的问题

以上都是些常见的问题,实际不同公司下,有些业务功能在实现上都还存在差异,不过这种就不在我们讨论的范围内了。

针对上述的这些主要问题,归纳总结一下,不难发现,在不同的环境中,对于同一个接口测试来说,测试过程的逻辑基本都是一样的,而造成不同环境用例无法复用的因素最主要有两个:

所调用到的服务域名地址不同,不同环境对应的域名是不同的。比如测试环境的域名为test.xxx.com,而正式环境的域名对应为www.xxx.com。

测试数据、配置数据不同,不同环境对应的测试数据和用到的配置数据存在不同。比如测试环境对应的用户ID为123456,但在正式环境对应的用户ID可能就变成了654321。

而针对不同环境,调用的服务域名地址不同,解决该问题的基本思路用两个关键词概括:抽象、枚举。

如何抽象,如何枚举,下面分别从测试框架(以Robot Frameowork框架为例)和语言实现层面(以Python语言)为大家逐一介绍。

1. 测试框架支持多环境运行思路

下述以Robot Framework框架为例,介绍如何实现一套测试用例支持多个不同运行环境,不同框架实现思路皆相通,其它框架可供参考借鉴。

在RF框架下,实现此类需求,总的原则是利用:外部变量文件+全局动态变量,将接口测试脚本中涉及传入域名的值统一封装抽离为一个统一的公共环境变量,并且将各个不同环境域名统一存放到一个公共环境配置变量文件中。

先来看一则脚本片段截图:

可以看到,在调用request_post关键字发起POST请求时,需要传入域名地址${URL}、接口路径${path}、接口参数${datas}等。而对于同一个接口,不管是在哪个环境下,接口路径${path}和接口参数${datas}都应该是一样的。而对于接口地址${URL},在不同环境中对应的值会有所不同。但从图中我们并没有发现${URL}变量定义的位置,它的值是从哪里传进来的呢?

关于接口地址${URL}变量值动态引入,通常有两种方式。

  • 通过外部变量文件引入。
  • 通过全局动态参数引入。

1.1 通过外部变量文件引入

(1)定义好接口测试实战项目的目录结构,在Resource | Lib 目录下,创建环境配置变量文件,文件名称定义为config.py,文件内容如下:

# coding=utf-8
# 环境配置文件

# 测试环境
# URL = 'https://test.xxx.com'

# 预发布环境
# URL = 'https://pre.xxx.com'

# 生产环境
URL = 'https://www.xxx.com'

在config.py环境配置文件中,定义各个不同环境(测试环境、预发布环境、生产环境)的服务域名地址,且变量名统一为URL。在运行接口测试时,保留当前需要运行测试用例的环境地址,其他环境变量注释掉即可。

在实际项目当中,config.py配置文件中的地址替换成真实的接口服务地址即可,例如,上述配置文件中保留了生产环境的地址,此时运行接口测试用例,则调用的为生产环境的接口测试。需要注意的是,在同一个项目下,不同环境下的接口服务地址需要采用相同的变量名称,定义好后,在Robot Framework测试脚本中直接通过${URL}变量形式来引用环境变量值。

(2)环境配置变量文件创建好后,选择Resource | Business| 业务资源文件,在资源文件Settings配置选项中选择Add Variables添加变量文件,依次选择config.py配置文件存储路径,如图所示。

(3)config.py变量文件导入成功后,当需要在不同环境下运行接口测试用例时,可在用例脚本不做任何变更的情况下,只需要更改config.py配置文件中的地址即可实现一键切换接口测试运行环境。

1.2 全局动态参数引入

通过外部变量文件的形式引入,虽然可以实现在测试脚本不做任何变更的前提下完成一套用例多套环境运行的目的,但每次在不同环境运行时,需要去环境变量文件中进行调整,虽然调整幅度较小(只需要进行注释),但仍然不是那么便捷。

在Robot Framework中还在一种更便捷灵活的方式来实现此目的,即通过全局参数变量引用形式来实现对应变量值的全局动态修改。而采用参数变量引用的形式来实现变量值的动态修改,也分为两种方式。

1) 第一种方式:Arguments参数栏

在RIDE编辑器Run运行标签下的Arguments参数栏中增加参数变量--variable key:value。如下图所示,增加了一个变量名为URL,变量值为https://test.xxx.com。

参数栏中增加变量的书写格式:

-v变量名:变量值或者--variable变量名:变量值。

上图中,参数栏填入-v URL:https://test.xxx.com,对URL变量赋值为https://test.xxx.com。这样在运行接口测试用例时,会将URL对应的变量值动态修改赋值为https://test.xxx.com。此时即使环境变量文件中的URL变量为https://www.xxx.com。通过这种命令行参数变量的引入形式仍然可以实现动态修改URL值。

通过参数变量--variable key:value形式引入的变量值,为全局变量优先级最高。

2) 第二种方式:命令行参数

采用Pybot或Robot命令行的形式来运行Robot Framework接口测试用例时,引入参数变量替换,例如:

Robot --variable URL:"https://test.xxx.com" /usr/local/rf_api || exit 0。

此种方式也是最为常用的调用形式,适合与CI持续集成系统对接。

2. 语言层面支持多环境运行思路

以Python语言为例,从语言层面解决如何一套用例支持多环境运行,本质还是要在用例层对测试环境无感,需要把环境所用的数据抽象出来。

随便画了一张草图,大家凑合着看,图中所示,是一个典型的桥接模式(Bridge Pattern):将抽象部分与实现部分分离,使它们都可以独立地变化。

拿上述最开始的代码示例来讲:需要抽象出服务地址账号两个对象,用例逻辑层只允许使用这些抽象的对象,而不能直接访问具体的数据,例如改成如下:

def test_login(self):
    requests.post(entrypoint.URL+"/login", data={"username":data.account.username, "password":data.account.password})

而在Python中,可以利用property装饰器来实现简易的桥接模式(其它语言也有类似的设计模式或实现),代码示例如下:

from enum import Enum

class Environment(Enum):
    DEV = 0
    TEST = 1
    PRE = 2
    PROD= 3

class EntryPoint:
    _ENV_URL = {
        Environment.DEV: "https://dev.xxx.com",
        Environment.TEST: "https://test.xxx.com",
        Environment.PRE: "https://pre.xxx.com",
        Environment.PROD: "https://www.xxx.com"
    }

    @property
    def URL(self):
        return self._ENV_URL[env]

env = Environment.DEV   # 作为全局的环境变量

样例代码中,先通过继承Enum类实现了一个枚举类Environment,在枚举类中定义了各环境的常量,可以理解是为后续定义各环境具体的Key值。

接着定义了一个EntryPoint类,并且在该类中,定义了一个存储各环境的字典,KEY名为枚举类中定义的常量。通过在URL方法 ,增加@property装饰器,可以让URL方法变成只读属性,并且通过obj.URL即可调用。

如果需要切换环境去执行,只要更新全局变量env就可以实现。

如果你对Python中的,Enum枚举用法和@property装饰器,还不了解或者想更深入了解这些用法,具体可参考官方文档介绍。

# @property用法官方文档
https://docs.python.org/3/library/functions.html#property
# Enum用法官方文档
https://docs.python.org/zh-cn/3/library/enum.html

受篇幅限制,一套自动化测试用例,不同环境对应的测试数据不同,解决思路下回再介绍,完整的自动化测试设计规范及各类实战技巧,建议可以系统性地学习:《自动化测试实战宝典:Robot Framework + Python 从小工到专家》一书中的内容。

新书京东订购传送门:https://item.jd.com/12629017.html#

如果觉得文章对你有所帮助,动动手点赞一下以表支持,你的肯定是我创作最大的鼓励和支持。

热文推荐 

更多原创干货除了发表于腾讯云+社区,第一时间也会发布至公众号「测试开发技术」(mikezhou_talk),欢迎关注!

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

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

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 微服务下的契约测试(CDC)解读

    有近两周没有在公众号中发表文章了,看过我之前公众号的读者都知道,公众号中近期在连载《RobotFramework接口自动化系列课程》,原本计划...

    测试开发技术
  • 推荐一款技术人必备的接口测试神器:Apifox

    作为互联网行业技术从业者,接口调试是必不可少的一项技能,通常我们都会选择使用 Postman 这类工具来进行接口调试,在接口调试方面 Postman 做的确实非...

    测试开发技术
  • 说说Python编码规范

    已有近两个月没有发表过文章了,前段时间外甥和女儿过来这边渡暑假,平常晚上和周末时间都陪着她们了,趁这个周末有空,再抽空再把这块拾起来。 ...

    测试开发技术
  • D3.js-散点图

    奋飛
  • ASP.NET的路由系统:URL与物理文件的分离

    表现为请求地址与目标Controller和Action的动态映射的URL路由系统并不是专属于ASP.NET MVC,而是直接建立在ASP.NET 中。ASP.N...

    蒋金楠
  • iOS下JS与OC互相调用(二)--WKWebView 拦截URL

    在上篇文章中讲述了使用UIWebView拦截URL的方式来处理JS与OC交互。 由于UIWebView比较耗内存,性能上不太好,而苹果在iOS 8中推出了WK...

    Haley_Wong
  • 【Rust日报】2020-04-12 - 如何在正确性至关重要的Rust项目中进行错误处理

    新增Apollo Federation网关协议的支持,用rust写基于graphql接口的微服务成为可能!改变用rust来写graphql只能做做玩具项目的现状...

    MikeLoveRust
  • RN沙龙 | 携程是如何做React Native优化的

    赵辛贵,携程无线开发高级技术经理,目前主要负责React Native框架在携程内部的使用推广和性能优化 React Native(下文简称RN)开源已经一年...

    携程技术
  • Python - 关于Python的变量

    True        //这是因为小的整数和字符串被缓存并复用了,是Python为了执行速度而采用的优化处理

    py3study
  • 【Rust日报】2020-02-06 那些在生产中使用 Rust 的公司

    按行业组织的,在生产中使用 Rust 的公司的精选列表。可供大家参考,GitHub 地址:https://github.com/omarabid/rust-co...

    MikeLoveRust

扫码关注云+社区

领取腾讯云代金券