pytest自动化测试框架tep环境变量、fixtures、用例三者之间的关系

2023-09-20 14:26:29

 tep是一款测试工具,在pytest测试框架基础上集成了第三方包,提供项目脚手架,帮助以写Python代码方式,快速实现自动化项目落地。

在tep项目中,自动化测试用例都是放到tests目录下的,每个.py文件相互独立,没有依赖,1个文件即1条用例,彼此分离。

虽然用例也能相互引用,但是除非万不得已,一般不建议这么做,牵一发动全身,后期维护困难。

用例的代码编写,思路是从上往下的,和pytest/unittest/script常规写法无异,不会有学习成本,一般也不会有问题。有成本有问题的可能是环境变量和fixtures,因为tep做了封装,提供了依赖注入的共享方式,fixture又是pytest较难理解的知识点,所以有必要通过本文来讲讲tep环境变量、fixtures、用例三者之间的关系,帮助理解,以便更灵活顺手的借助tep实现pytest自动化项目。

假如不用环境变量和fixtures
假如不用环境变量和fixtures,是完全可以的!比如,在tests下新建脚本login_test.py:

from tep.client import request
 
def test():
    response = request("post",
            url="https://qa.com/api/users/login",
            headers={"Content-Type": "application/json"},
            json={
                "username": "admin",
                "password": "123456",
            }
        )
    assert response.status_code < 400

请求接口https://qa.com/api/users/login,断言响应状态码小于400。问题来了:url固定,假如需要切换两个环境qa和release,该怎么办?

参数化
无论是做自动化测试还是性能测试,都会接触到参数化这个词。它是指把代码中的固定数据(硬编码)定义成变量,让每次运行时数据不一样,固定数据变为动态数据。动态数据的来源是变量、数据库、外部文件等。动态数据的类型一般是常量的字符串,也可以是函数,比如JMeter的函数助手,也可以是依赖注入,比如pytest的fixture。

依赖注入的fixture
“依赖注入是控制反转(IoC, Inversion of Control)的一种技术形式”,这句话出自维基百科,我也不知道什么意思,画个图简单表达下:

意思是,给client一个injector,client不需要做什么,就能用到service。

pytest的fixture实现了依赖注入,允许我们在不修改测试代码的情况下,引入fixture来额外添加一些东东。

对于url来说,域名是需要做参数化的,不同环境域名不同,所以tep把它做成了fixture,通过函数参数引入:

from tep.client import request
from tep.fixture import *
 
def test(url):  # 引入fixture
    response = request("post",
            url=url("/api/users/login"),
            headers={"Content-Type": "application/json"},
            json={
                "username": "admin",
                "password": "123456",
            }
        )
    assert response.status_code < 400

tep.fixture.url定义如下:

@pytest.fixture(scope="session")
def url(env_vars):
    def domain_and_uri(uri):
        if not uri.startswith("/"):
            uri = "/" + uri
        return env_vars.domain + uri
 
    return domain_and_uri

如果一眼就看懂了,恭喜你,如果一眼就看懵了,没关系。我会花功夫把它讲明白,它很关键!

把fixture当变量看

虽然从定义上看,fixture是用def关键字定义的函数,但是理解上把它看做变量就可以了。比如:

import pytest
 
 
@pytest.fixture
def name():
    return "dongfanger"

一般函数的用法是函数名加小括号,通过name()才能得到"dongfanger"。fixture不一样,以上定义可以理解为:

name = "dongfanger"

"dongfanger"赋值给name,fixture名 = return值。通过变量name就得到"dongfanger"了。

既然是变量,那么就能随便赋值,strfunctionclassobject都行。比如在fixture内部定义个函数:

import pytest
 
 
@pytest.fixture
def who():
    def get_name():
        return "dongfanger"
    return get_name

理解为把函数名get_name赋值给fixture名变量:

who = get_name

get_name是个函数名,需要加小括号get_name()才能得到"dongfanger"who也必须通过who()才能得到"dongfanger"。再看tep.fixture.url是不是清楚些了:

@pytest.fixture(scope="session")
def url(env_vars):
    def domain_and_uri(uri):
        if not uri.startswith("/"):
            uri = "/" + uri
        return env_vars.domain + uri
 
    return domain_and_uri

理解为把函数名domain_and_uri赋值给fixture名变量:

url = domain_and_uri

使用时通过url("/api")得到域名和uri拼接后的结果。

第2行的def url(env_vars):也有一个参数env_vars,接下来继续解释。

fixture参数是其他fixture

fixture的参数只能是其他fixture。比如:

import pytest
 
 
@pytest.fixture
def chinese_name():
    return "东方er"
 
 
@pytest.fixture
def english_name(chinese_name):
    return "dongfanger"

调用english_name,pytest会先执行参数里的其他fixture chinese_name,然后执行自己english_name

如果把tep.fixture.url拆成两步来看,就很清晰了,第一步:

@pytest.fixture(scope="session")
def url(env_vars):
    func = None
    return func

第二步:

@pytest.fixture(scope="session")
def url(env_vars):
    func = None
    
    
    def domain_and_uri(uri):
        if not uri.startswith("/"):
            uri = "/" + uri
        return env_vars.domain + uri
 
    
    func = domain_and_uri
    return func

环境变量

tep.fixture.url的参数是另外一个fixture env_vars 环境变量,它的定义如下:

from tep.fixture import *
 
 
@pytest.fixture(scope="session")
def env_vars(config):
    class Clazz(TepVars):
        env = config["env"]
 
        """Variables define start"""
        # Environment and variables
        mapping = {
            "qa": {
                "domain": "https://qa.com",
            },
            "release": {
                "domain": "https://release.com",
            }
            # Add your environment and variables
        }
        # Define properties for auto display
        domain = mapping[env]["domain"]
        """Variables define end"""
 
    return Clazz()

只看中间注释"""Variables define start""""""Variables define end"""部分即可。url参数化的域名就在这里,mapping字典建立了环境和变量之间的映射,根据不同的环境key,获取不同的变量value。

config fixture的作用是读取conf.yaml文件里面的配置。

参数化的方式很多,JMeter提供了4种参数化方式,tep的fixture env_vars借鉴了JMeter的用户自定义变量:

 env_vars.put()env_vars.get()借鉴了JMeter BeanShell的vars.put()vars.get()

实例:测试多个网址

讲到最后,形成了思路,通过实际的例子,看看环境变量、fixtures、用例是怎么用起来的,加深下印象。假如qa环境有2个网址,学校端和机构端,脚本都需要用到。

第一步修改env_vars,编辑fixture_env_vars.py

        """Variables define start"""
        # Environment and variables
        mapping = {
            "qa": {
                "domain": "https://qa.com",
                "domain_school": "https://school.qa.com",  # 新增
                "domain_org": "https://org.qa.com"  # 新增
            },
            "release": {
                "domain": "https://release.com",
                "domain_school": "https://school.release.com"  # 新增
                "domain_org": "https://org.release.com"  # 新增
            }
            # Add your environment and variables
        }
        # Define properties for auto display
        domain = mapping[env]["domain"]
        domain_school = mapping[env]["domain_school"]  # 新增
        domain_org = mapping[env]["domain_org"]  # 新增
        """Variables define end"""

添加了6行代码,定义了env_vars.domain_schoolenv_vars.domain_org

第二步定义fixtures,新建fixture_url.py

@pytest.fixture(scope="session")
def url_school(env_vars):
    def domain_and_uri(uri):
        if not uri.startswith("/"):
            uri = "/" + uri
        return env_vars.domain_school + uri
 
    return domain_and_uri
    
 
@pytest.fixture(scope="session")
def url_org(env_vars):
    def domain_and_uri(uri):
        if not uri.startswith("/"):
            uri = "/" + uri
        return env_vars.domain_org + uri
 
    return domain_and_uri

参照tep.fixture.url,修改env_vars.domain为env_vars.domain_school和env_vars.domain_org,新增了2个fixture url_school和url_org。

更进一步,也许会定义fixture login_school和login_org,灵活选择。

小结
本文循序渐进的讲解了tep环境变量、fixtures和用例之间的关系,重点对tep.fixture.url进行了解释,只要理解了它,整体关系就很清楚了。之所以要用fixture,原因一是多人协作共享,我们需要用别人写好的函数,复用返回值,有些同学习惯定义函数参数,参数不变还好,万一哪天改了,别人引用的用例会全部报错,fixture很好的限制了这一点,它默认是不能传参的,虽然可以通过定义内部函数来实现传参,但是并不推荐这么做,宁愿增加冗余代码,定义多个fixture,也比代码耦合度高好一些。原因二是import的问题,pytest会自动查找conftest.py里的fixture,tep会进一步自动查找fixtures下的fixture导入到conftest.py,不需要import就能使用,减少了import代码,避免了可能会出现的循环导入问题。
 下面是配套学习资料,对于做【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!

更多推荐

Kubernetes-01-基础概念篇 基础组件&搭建一个K8S集群

K8S重要技术内容主要涵盖:集群架构、容器化应用部署、ScaleUp/Down、滚动更新、监控检查、集群网络、数据管理、监控与日志一、基础名词1.ClusterCluster是计算、存储、网络资源的集合,利用资源运行各种基于容器的应用2.MasterMaster是Cluster的核心,负责调度、控制。高可用版本,一般至

Kubernetes(K8s)上使用分布式存储(Distributed Storage)

摘要在Kubernetes(K8s)上使用分布式存储(DistributedStorage)是一种常见的方案,它可以为集群中的应用程序提供持久性和可扩展性。以下是在Kubernetes上使用分布式存储的说明:存储类(StorageClass):首先,你需要创建一个Kubernetes的存储类,用于定义分布式存储的属性和

KubeSphere Namespace 数据删除事故分析与解决全记录

作者:宇轩辞白,运维研发工程师,目前专注于云原生、Kubernetes、容器、Linux、运维自动化等领域。前言2023年7月23日在项目上线前夕,K8s生产环境出现故障,经过紧急修复之后,K8s环境恢复正常;另外我们环境引入了KubeSphere云原生平台技术,为了方便研发人员对于K8s权限的细粒度管理,我方手动将K

K8S的CKA考试环境和题目

CKA考试这几年来虽然版本在升级,但题目一直没有大的变化,通过K8S考试的方法就是在模拟环境上反复练习,通过练习熟悉考试环境和考试过程中可能遇到的坑。这里姚远老师详细向大家介绍一下考试的环境和题目,需要详细资料的同学请在文章后面留言。祝大家考试成功。K8S的考试环境CKA考试环境由三台虚拟机组成,这三台虚拟机姚远老师已

3.k8s dashboard设置域名登录案例(ingress版本为1.3.1)

文章目录前言一、安装ingress1.1下载ingress部署文件1.2查看是否安装成功二、配置dashboard域名映射2.1.在windows和linux添加上域名映射2.2生成tls证书2.3新增ingress配置2.3验证总结前言前面搭建了集群,配置了账号密码登录,现在配置k8sdashboard的域名登录,这

Augmented Large Language Models with Parametric Knowledge Guiding

本文是LLM系列文章,针对《AugmentedLargeLanguageModelswithParametricKnowledgeGuiding》的翻译。参数知识引导下的增强大型语言模型摘要1引言2相关工作3LLM的参数化知识引导4实验5结论摘要大型语言模型(LLM)凭借其令人印象深刻的语言理解和生成能力,显著提高了自

GPT4RoI: Instruction Tuning Large Language Model on Region-of-Interest

在图像-文本对上调整大语言模型(LLM)的指令已经实现了前所未有的视觉-语言多模态能力。然而,他们的视觉语言对齐仅建立在图像级别上,缺乏区域级别对齐限制了他​​们在细粒度多模态理解方面的进步。在本文中,我们提出对感兴趣区域进行指令调整。关键设计是将边界框重新表述为空间指令的格式。将空间指令和语言嵌入提取的视觉特征的交错

计算机网络基础知识(非常详细)

1.网络模型1.1OSI七层参考模型七层模型,亦称OSI(OpenSystemInterconnection)参考模型,即开放式系统互联,是网络通信的标准模型。一般称为OSI参考模型或七层模型。它是一个七层的、抽象的模型体,不仅包括一系列抽象的术语或概念,也包括具体的协议。物理层:负责传输原始的比特流,数模转换、模数转

Postman应用——Collection、Folder和Request

文章目录Collection新建CollectionCollection重命名保存Request到Collection在Collection下创建Request删除CollectionFolder新建FolderFolder重命名保存Request到Folder在Folder下创建Request在Folder下创建Fo

MySQL数据库详解 一:安装MySQL数据库及基本管理

文章目录1.数据库的基本概念1.1数据库的组成1.1.1数据1.1.2表1.1.3数据库1.2当前主流数据库及其特点1.3数据库类型1.3.1关系数据库1.3.1.1关系数据库的组成1.3.1.2非关系数据库2.安装MySQL2.1yum安装2.2编译安装MySQL2.2.1前置准备2.2.2编译安装2.2.3修改my

成集云 | 用友U8集成聚水潭ERP(用友U8主管供应链)| 解决方案

源系统成集云目标系统方案介绍用友U8是一套企业级的解决方案,可满足不同的制造、商务模式下,不同运营模式下的企业经营管理。它全面集成了财务、生产制造及供应链的成熟应用,并延伸客户管理至客户关系管理(CRM),以及零售、分销领域实现了全面整合。聚水潭是一款以SaaSERP为核心,集多种商家服务为一体的SaaS协同平台,为全

热文推荐