pytest实现用例间参数传递的方式

pytest实现用例间参数传递的方式

我们在做接口自动化测试的时候,会经常遇到这种场景:接口A的返回结果中的某个字段,是接口B的某个字段的入参。如果是使用postman,那我们可以通过设置后置变量,然后在需要使用的地方通过{{}}的方式来进行调用。但是如果是使用自己写的测试框架中要如何实现呢?我想到的是如下三种方法。

首先说明一下,以下三种方式均是通过python + pytest来实现的

一、通过conftest创建全局变量

conftest.py文件是pytest框架中很有用的一个东西,首先看下官方文档中的解释:

大概意思就是说,conftest.py文件供整个用例目录(conftest.py文件可以有多个,并且只在当前package下生效)使用而无需导入,也就是说在用例目录是conftest中的信息是公用的,例如:

python 复制代码
a/conftest.py:
    def pytest_runtest_setup(item):
        # called for running each test in 'a' directory
        print("setting up", item)
 
a/test_sub.py:
    def test_sub():
        pass
 
test_flat.py:
    def test_flat():
        pass
 

运行后的结果:

python 复制代码
pytest test_flat.py --capture=no  # will not show "setting up"
pytest a/test_sub.py --capture=no  # will show "setting up"

而我们就可以通过conftest + fixture函数来实现我们想要的效果了,具体代码如下:`

python 复制代码
# conftest.py
 
# 定义一个全局变量,用于存储内容
global_data = {}
 
@pytest.fixture
def set_global_data():
    """
    设置全局变量,用于关联参数
    :return:
    """
 
    def _set_global_data(key, value):
        global_data[key] = value
 
    return _set_global_data
 
@pytest.fixture
def get_global_data():
    """
    从全局变量global_data中取值
    :return:
    """
 
    def _get_global_data(key):
        return global_data.get(key)
 
    return _get_global_data
 
 

简单说一下实现逻辑:

首先定义一个变量global_data用于接收存储用例返回的结果

set_global_data和get_global_data两个fixture方法顾名思义,set方法是往global_data中存数据,get方法是从global_data中取数据

方法实现了,具体应该怎么使用呢?如下:

python 复制代码
 
# test_get_set.py
 
import requests
import pytest
 
 
 
def test_set(set_global_data):
    res = requests.get("http://www.baidu.com")
    status_code = res.status_code
    logger.info(f"请求返回状态码:{status_code}")
    set_global_data("status_code", status_code)
 
 
def test_get(get_global_data):
    data = get_global_data("status_code")
    logger.info(f'通过get_global_data方法获取的值:{data}')
 
 
if __name__ == '__main__':
    pytest.main(['-sv', 'test_get_set.py'])

返回结果:

python 复制代码
test_get_set.py::test_set PASSED
2021-12-24 17:58:37.642 | INFO     | cases.test_get_set:test_set:19 - 请求返回状态码:200
2021-12-24 17:58:37.643 | INFO     | cases.test_get_set:test_get:25 - 通过get_global_data方法获取的值:200
test_get_set.py::test_get PASSED
 
============================== 2 passed in 0.06s ===============================

通过这种方式,便实现了用例间的参数传递问题。

在实际工作中,因为涉及到的接口、用例会很多,所以可以根据需要使用不同的conftest进行管理。并且存储的数据结构也需要进行规范区分,如使用方法名作为字典的key。

二、使用tmpdir_factory方法

第二种方法,是使用pytest的tmpdir和tmpdir_factory两个夹具函数,同样是通过conftest文件来实现。仍然是先来看下官方文档针对这两个方法的说明:

简单来说,这两个方法的作用就是为每个测试方法创建一个临时目录用于存储自定义的文件,这个临时目录会默认保存3个sessions,之后就会按照创建的顺序删除旧的目录。看下官方的例子:

python 复制代码
# content of test_tmpdir.py
def test_create_file(tmpdir):
    p = tmpdir.mkdir("sub").join("hello.txt")
    p.write("content")
    assert p.read() == "content"
    assert len(tmpdir.listdir()) == 1
    assert 0
python 复制代码
# contents of conftest.py
import pytest
 
 
@pytest.fixture(scope="session")
def image_file(tmpdir_factory):
    img = compute_expensive_image()
    fn = tmpdir_factory.mktemp("data").join("img.png")
    img.save(str(fn))
    return fn
 
 
# contents of test_image.py
def test_histogram(image_file):
    img = load_image(image_file)
    # compute and test histogram

我在实际项目中的使用:

仍是在conftest.py文件中自定义一个夹具函数,返回结果是一个元组,p是tmpdir_factory方法返回的对象,转为字符串之后就是文件存储的路径。

自定义一个名为"apitest-tmp-dir"的文件夹用于存储文件

python 复制代码
# conftest.py
 
@pytest.fixture
def tmp_factory(tmpdir_factory):
    """
    生成临时目录
    """
    p = tmpdir_factory.mktemp('apitest-tmp-dir')
    logger.info("当前临时文件的目录为:" + str(p))
    return p, str(p)

在测试方法中的使用

python 复制代码
 
# test_get_set.py
 
import requests
import pytest
import json
 
 
def test_set(tmp_factory):
    res = requests.get("http://www.baidu.com")
    status_code = res.status_code
    logger.info(f"返回状态码:{status_code}")
 
    logger.debug(tmp_factory)
    # 创建test_set.txt文件
    a = tmp_factory[0].join("test_set.txt")
    # 将需要的内容写入到文件中
    a.write({"status_code": status_code})
    
    # 使用read()方法获取文件中的内容
    logger.debug(a.read())
 
 
 
if __name__ == '__main__':
    pytest.main(['-sv', 'test_get_set.py'])

返回结果:

python 复制代码
test_get_set.py::test_set 2021-12-24 18:24:39.292 | INFO     | cases.conftest:tmp_factory:150 - 当前临时文件的目录为:/private/var/folders/_f/1d0lt83x1599bf6mcfppbwp40000gn/T/pytest-of-j/pytest-19/apitest-tmp-dir0
2021-12-24 18:24:39.347 | INFO     | cases.test_get_set:test_set:32 - 返回状态码:200
2021-12-24 18:24:39.347 | DEBUG    | cases.test_get_set:test_set:34 - (local('/private/var/folders/_f/1d0lt83x1599bf6mcfppbwp40000gn/T/pytest-of-j/pytest-19/apitest-tmp-dir0'), '/private/var/folders/_f/1d0lt83x1599bf6mcfppbwp40000gn/T/pytest-of-j/pytest-19/apitest-tmp-dir0')
2021-12-24 18:24:39.348 | DEBUG    | cases.test_get_set:test_set:38 - {'status_code': 200}
PASSED
 
============================== 1 passed in 0.07s ===============================

创建的文件:

可以看到,tmpdir_factory会自动为我们创建一个目录,名字是tmp_factory方法中自定义的名称后面加0,同时它的上级目录会自动从pytest-0递增

说下这个办法的优缺点:

  • 先说优点,这种数据存储是直接写入到文件,因此即使运行结束后也可以访问,而不像第一种方法存储的内容会随着用例运行的结束而消失
  • 再说缺点,因为这个临时目录最多只能保存3个,因此如果用例很多时,那么就可能存在文件被自动删除的风险。不过这个貌似可以通过修改默认配置来解决,可以继续研究下。
    缺点二,上面的例子中,直接通过a.read()就访问到了文件中的内容,这是因为内容的存储与读取全是在一个方法内,因此可以直接调用。如果是A方法存结果,在B中需要读取,那么便不能直接使用read()方法了(因为每个方法都会创建一个目录,并且默认的读取地址都是这个方法自己创建的目录)。就需要我们自己去单独封装一个读取文件的方法了,实现起来也不难而且tmpdir本身的这些方法也是对os.path一些方法的二次封装。

话说回来,都需要我自己去封装一个读取文件的方法了,为啥不干脆把读、写都自己来做呢?这样是否删除文件、删除几个、什么时候删除就完全由自己定义了啊,貌似会更方便。。。。。

tmp_factory本身就是一个fixture函数,那么它就可以设定作用域function、class、module、session,设定之后是不是就可以解决文件覆盖的问题呢?在经过测试之后果然解决了问题,只要将tmp_factory的作用域改为session,那么就只会生成一个文件夹,至于文件全是根据用例名称创建的,完全没有影响的。只要再封装一个文件读取的函数就解决所有问题了。

相关推荐
努力搬砖的咸鱼1 天前
从零开始搭建 Pytest 测试框架(Python 3.8 + PyCharm 版)
python·pycharm·pytest
FINE!(正在努力!)3 天前
PyTest框架学习
学习·pytest
程序员杰哥4 天前
接口自动化测试之pytest 运行方式及前置后置封装
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·pytest
测试老哥4 天前
Pytest+Selenium UI自动化测试实战实例
自动化测试·软件测试·python·selenium·测试工具·ui·pytest
水银嘻嘻4 天前
07 APP 自动化- appium+pytest+allure框架封装
python·appium·自动化·pytest
天才测试猿5 天前
接口自动化测试之pytest接口关联框架封装
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·pytest
not coder6 天前
Pytest Fixture 详解
数据库·pytest
not coder6 天前
pytest 常见问题解答 (FAQ)
开发语言·python·pytest
程序员的世界你不懂6 天前
(1)pytest简介和环境准备
pytest
not coder6 天前
Pytest Fixture 是什么?
数据库·oracle·pytest