IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发测试 -> pytest-fixture应用 -> 正文阅读

[开发测试]pytest-fixture应用

fixture固定装置,是pytest用于将测试前后进行预备、清理工作的代码分离出核心测试逻辑的一种机制。
《pytest测试实战》学习并进行整理输出,所以是依照书中的例子进行学习和实践。

代码下载路径
链接:https://pragprog.com/titles/bopytest/python-testing-with-pytest/
在这里插入图片描述

一、fixture存放位置

1.fixture可以定义在测试文件中
2.定义在conftest.py中,供所在目录及子目录下的测试使用

尽管conftest.py是Python模块,但它不能被测试文件导入。import conftest用法是不允许出现的。conftest.py被pytest视作一个本地插件库。

二、fixture使用

1.使用fixture执行配置及销毁逻辑

测试用例中需要预先完成Tasks数据库的配置及启动,以及测试完毕后清理以及断开数据库连接。

import pytest
import tasks
from tasks import Task

@pytest.fixture()
def tasks_db(tmpdir):
    """connect to db before tests,disconnect after."""
    # Setup : start db
    tasks.start_tasks_db(str(tmpdir), 'tiny')
    yield  # this is where  the testing happens
    # Teardown : stop db
    tasks.stop_tasks_db()

当测试函数引用此fixture后,那么运行测试用例之前会运行tasks.start_tasks_db(str(tmpdir), ‘tiny’),当运行到yield时,系统将停止fixture的运行,转而运行测试函数,等测试函数运行完成之后再回到fixture运行yield之后的代码。

2.使用–setup-show回溯fixture的执行过程

使用–setup-show可以看到测试过程中执行的什么,以及执行的先后顺序。
在这里插入图片描述
fixture前面的F和S代表了fixture的作用范围。F代表函数级别的作用范围,S代表会话级别的作用范围

3.使用fixture传递测试数据

fixture非常适合存放测试数据,并且它能返回任何数据类型。下面代码中fiixture函数a_tuple=(1, ‘foo’, None, {‘bar’: 23})
test_a_tuple测试函数对a_tuple中的数据进行断言

@pytest.fixture()
def a_tuple():
    """Return something more interesting."""
    return (1, 'foo', None, {'bar': 23})
    
def test_a_tuple(a_tuple):
    """Demo the a_tuple fixture."""
    assert a_tuple[3]['bar'] == 32

在这里插入图片描述
当fixture内部出现错误时,会报ERROR而不是FAIL,用户就会知道失败不是发生在核心测试函数中,而是发生在测试所依赖的fixture

4.使用多个fixture

下面为tasks_proj/tests/conftest.py代码,编写了四个fixture。
前两个fixture返回了包含多个属性task的对象数据。
后两个fixture的形参中各有两个fixture(tasks_db和返回数据集的fixture),数据集用于向数据库中添加task数据记录,这样测试时就可以使用测试库数据,而非空数据库。

@pytest.fixture()
def tasks_just_a_few():
    """All summaries and owners are unique."""
    return (
        Task('Write some code', 'Brian', True),
        Task("Code review Brian's code", 'Katie', False),
        Task('Fix what Brian did', 'Michelle', False))

@pytest.fixture()
def tasks_mult_per_owner():
    """Several owners with several tasks each."""
    return (
        Task('Make a cookie', 'Raphael'),
        Task('Use an emoji', 'Raphael'),
        Task('Move to Berlin', 'Raphael'),

        Task('Create', 'Michelle'),
        Task('Inspire', 'Michelle'),
        Task('Encourage', 'Michelle'),

        Task('Do a handstand', 'Daniel'),
        Task('Write some books', 'Daniel'),
        Task('Eat ice cream', 'Daniel'))

@pytest.fixture()
def db_with_3_tasks(tasks_db, tasks_just_a_few):
    """Connected db with 3 tasks, all unique."""
    for t in tasks_just_a_few:
        tasks.add(t)

@pytest.fixture()
def db_with_multi_per_owner(tasks_db, tasks_mult_per_owner):
    """Connected db with 9 tasks, 3 owners, all with 3 tasks."""
    for t in tasks_mult_per_owner:
        tasks.add(t)

位于tasks_proj/tests/func/test_add.py中的测试函数,使用fixture函数db_with_3_tasks初始化数据库并包含三条数据,使用tasks.add()添加一条数据后,判断数据库中tasks对象的总量是否为4.

def test_add_increases_count(db_with_3_tasks):
    """Test tasks.add() affect on tasks.count()."""
    # GIVEN a db with 3 tasks
    #  WHEN another task is added
    tasks.add(Task('throw a party'))

    #  THEN the count increases by 1
    assert tasks.count() == 4

运行结果:
pytest --setup-show ./tests/func/test_add.py::test_add_increases_count
在这里插入图片描述

5.制定fixture作用范围

fixture包含了一个scope的可选参数,用于控制fixture执行配置和销毁逻辑的频率。@pytest.fixture()的scope参数有四个待选值:function、class、module、session(默认值为function)

  • scope=‘function’
    函数级别的fixture每个测试函数只需要运行一次。配置代码在测试用例运行之前运行,销毁代码在测试用例运行后运行。
  • scope=‘class’
    类级别的fixture每个测试类只需要运行一次。无论测试类里有多少个类方法,都可以共享这个fixture。
  • scope=‘module’
    模块级别的fixture每个模块只需要运行一次。无论模块里有多少个测试函数、类方法或其他fixture都可以共享这个fixture。
  • scope=‘session’
    会话级别的fixture每次会话只需要运行一次。一次pytest会话中的所有测试函数、方法都可以共享这个fixture。
"""Demo fixture scope."""
import pytest

@pytest.fixture(scope='function')
def func_scope():
    """A function scope fixture."""

@pytest.fixture(scope='module')
def mod_scope():
    """A module scope fixture."""

@pytest.fixture(scope='session')
def sess_scope():
    """A session scope fixture."""

@pytest.fixture(scope='class')
def class_scope():
    """A class scope fixture."""

def test_1(sess_scope, mod_scope, func_scope):
    """Test using session, module, and function scope fixtures."""

def test_2(sess_scope, mod_scope, func_scope):
    """Demo is more fun with multiple tests."""

@pytest.mark.usefixtures('class_scope')
class TestSomething():
    """Demo class scope fixtures."""

    def test_3(self):
        """Test using a class scope fixture."""

    def test_4(self):
        """Again, multiple tests are more fun."""

执行结果:
在这里插入图片描述
注:
1.scope参数是在定义fixture时定义的,而不是在调用fixture时定义的。因此,使用fixture的测试函数是无法改变fixture的作用范围的
2.fixture只能使用同级别的fixture,或比自己级别更高的fixture。譬如函数级别的fixture可以使用同级别的fixture,也可以使用类级别、模块级别、会话级别的fixture,反之则不行。

6.使用usefixture指定fixture

之前都是在测试函数的参数列表中制定fixture,也可以使用@pytest.mark.usefixtures(‘fixture1’,‘fixture2’)标记测试函数或类。这对测试函数来说意义不大,非常适合测试类。
test_scope.py内容:

@pytest.mark.usefixtures('class_scope')
class TestSomething():
    """Demo class scope fixtures."""
    def test_3(self):
        """Test using a class scope fixture."""
    def test_4(self):
        """Again, multiple tests are more fun."""

使用usefixtures和在测试方法中添加fixture参数,两者大体上差不多。区别之一在于只有后者才能使用fixture的返回值

7.为常用fixture添加autouse选项

通过指定autouse=True选项,使作用域内的测试函数都运行该fixture。

"""Demonstrate autouse fixtures."""
import pytest
import time

@pytest.fixture(autouse=True, scope='session')
def footer_session_scope():
    """Report the time at the end of a session."""
    yield
    now = time.time()
    print('--')
    print('finished : {}'.format(time.strftime('%d %b %X', time.localtime(now))))
    print('-----------------')

@pytest.fixture(autouse=True)
def footer_function_scope():
    """Report test durations after each function."""
    start = time.time()
    yield
    stop = time.time()
    delta = stop - start
    print('\ntest duration : {:0.3} seconds'.format(delta))

def test_1():
    """Simulate long-ish running test."""
    time.sleep(1)

def test_2():
    """Simulate slightly longer test."""
    time.sleep(1.23)

执行结果:
在这里插入图片描述

8.为fixture重命名

使用@pytest.fixture()的name参数对fixture进行重命名。
test_rename_fixture.py代码:

"""Demonstrate fixture renaming."""
import pytest

@pytest.fixture(name='lue')
def ultimate_answer_to_life_the_universe_and_everything():
    """Return ultimate answer."""
    return 42
    
def test_everything(lue):
    """Use the shorter name."""
    assert lue == 42

运行结果:
在这里插入图片描述
pytest指定–fixtures命令行选项,可以查出fixture在哪个文件定义的,并且给出fixture定义时给出的定义内容(""" “”""中的内容)
命令:
D:\wy_only\tasks_proj\tests\func>pytest --fixtures test_rename_fixture.py
在这里插入图片描述
注:我在运行此命令时,返回的是所有pytest内置的fixture、conftest.py中的fixture,以及自身调用的fixture

9.*fixture的参数化

fixture做参数化处理,@pytest.fixture()中使用参数params=tasks_to_try,tasks_to_try可以是对象列表。test_add_variety2.py代码如下:

"""Test the tasks.add() API function."""
import pytest
import tasks
from tasks import Task

tasks_to_try = (Task('sleep', done=True),
                Task('wake', 'brian'),
                Task('breathe', 'BRIAN', True),
                Task('exercise', 'BrIaN', False))
                
task_ids = ['Task({},{},{})'.format(t.summary, t.owner, t.done)
            for t in tasks_to_try]
            
def equivalent(t1, t2):
    """Check two tasks for equivalence."""
    return ((t1.summary == t2.summary) and
            (t1.owner == t2.owner) and
            (t1.done == t2.done))

@pytest.fixture(params=tasks_to_try)
def a_task(request):
    """Using no ids."""
    return request.param

def test_add_a(tasks_db, a_task):
    """Using a_task fixture (no ids)."""
    task_id = tasks.add(a_task)
    t_from_db = tasks.get(task_id)
    assert equivalent(t_from_db, a_task)

其中fixture函数a_task的参数request也是pytest内建fixture之一。代表fixture的调用状态,它有一个param字段。会被@pytest.fixture(params=tasks_to_try)的params列表中的一个元素填充。即a_task返回的request.param是一个Task对象(如:Task(‘sleep’, done=True),这里比较难理解)。

其实a_task的逻辑是仅以request.param作为返回值供测试使用。因为task对象列表(tasks_to_try)包含了四个task对象,所以a_task将被测试调用4次。
运行结果:
在这里插入图片描述
由于未指定id,pytest用fixture名+一串数字作为task标识。可以在@pytest.fixture()中使用ids参数进行id设置,但是ids必须是一串字符串列表。task_ids定义了字符串列表的格式Task({},{},{}),譬如:Task(sleep,None,True)

task_ids = ['Task({},{},{})'.format(t.summary, t.owner, t.done)
            for t in tasks_to_try]
            
@pytest.fixture(params=tasks_to_try, ids=task_ids)
def b_task(request):
    """Using a list of ids."""
    return request.param

def test_add_b(tasks_db, b_task):
    """Using b_task fixture, with ids."""
    task_id = tasks.add(b_task)
    t_from_db = tasks.get(task_id)
    assert equivalent(t_from_db, b_task)

运行结果:
在这里插入图片描述
ids参数也可以被指定为一个函数,该函数id_func()将作用于params列表中的每一个元素。params是一个task对象列表,id_func()将调用单个task对象。

def id_func(fixture_value):
    """A function for generating ids."""
    t = fixture_value
    return 'Task({},{},{})'.format(t.summary, t.owner, t.done)


@pytest.fixture(params=tasks_to_try, ids=id_func)
def c_task(request):
    """Using a function (id_func) to generate ids."""
    return request.param


def test_add_c(tasks_db, c_task):
    """Use fixture with generated ids."""
    task_id = tasks.add(c_task)
    t_from_db = tasks.get(task_id)
    assert equivalent(t_from_db, c_task)

运行结果:
在这里插入图片描述
注:对测试函数进行参数化处理,可以多次运行的只有测试函数,而使用参数化fixture,每个使用该fixture的测试函数都可以被运行多次,fixture的这一特性非常强大

三、问题锦集

问题1:执行时报错“ModuleNotFoundError: No module named ‘tasks’”
在这里插入图片描述
解决方案:使用import tasks或from tasks import something ,需要在本地使用pip 安装tasks包(源代码)。进入到tasks_proj根目录。运行以下命令进行安装操作
命令:
pip install . 或者pip install -e .
在这里插入图片描述
在这里插入图片描述

  开发测试 最新文章
pytest系列——allure之生成测试报告(Wind
某大厂软件测试岗一面笔试题+二面问答题面试
iperf 学习笔记
关于Python中使用selenium八大定位方法
【软件测试】为什么提升不了?8年测试总结再
软件测试复习
PHP笔记-Smarty模板引擎的使用
C++Test使用入门
【Java】单元测试
Net core 3.x 获取客户端地址
上一篇文章      下一篇文章      查看所有文章
加:2022-04-04 12:40:45  更:2022-04-04 12:41:46 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/18 0:31:39-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码