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 dbtasks.start_tasks_db(str(tmpdir), 'tiny')yield  # this is where  the testing happens# Teardown : stop dbtasks.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 addedtasks.add(Task('throw a party'))#  THEN the count increases by 1assert 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."""yieldnow = 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()yieldstop = time.time()delta = stop - startprint('\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 42def 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 Tasktasks_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.paramdef 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.paramdef 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_valuereturn '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.paramdef 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-fixture应用相关推荐

  1. pytest fixture执行顺序

    module和function的调用顺序: import pytest@pytest.fixture(scope="module", params=["mod1" ...

  2. Pytest fixture参数化params

    unittest使用ddt来实现测试用例参数化.或parameterized实现测试用例参数化,pytest测试用例里面对应的参数可以用 parametrize 实现参数化,今天我们来了解下fixtu ...

  3. Pytest fixture之request传参

    Pytest中我们经常会用到数据参数化,我们来介绍下装饰器@pytest.fixture()配合request传参的使用 user = request.param 如果想把登录操作放到前置操作里,也就 ...

  4. Python Pytest调用fixture之@pytest.mark.usefixtures()、叠加usefixtures、@pytest.fixture(autouse=True)用法详解

    usefixtures与传fixture区别  如果fixture有返回值,那么usefixture就无法获取到返回值,这个是装饰器usefixture与用例直接传fixture参数的区别. 当fix ...

  5. Python pytest框架之@pytest.fixture()和conftest详解

    一.fixture简介 学pytest就不得不说fixture,fixture是pytest的精髓所在,类似unittest中setup/teardown这种前后置东西.但是比它们要强大.灵活很多,它 ...

  6. Pytest fixture实现测试用例前置后置操作

    大家好,我是杨叔.每天进步一点点,关注微信公众号[程序员杨叔],了解更多测试开发技术知识! 一.背景 自动化测试时必然会碰到用例执行前,需要做一些前置操作,以及用例执行后,需要做一些后置操作的时候.本 ...

  7. pytest.fixture如何像testng的beforeMethod一样使用

    熟悉使用testng的童鞋们应该都晓得beforeMethod,在测试脚本执行时,可以对每个测试函数进行初始化. 比如app的启动,浏览器的启动等,都可以定义到beforemethod里面. /*** ...

  8. 用pytest.fixture处理接口自动化跨文件token传参

    大部分的接口都需要在headers中传入token参数,原来的方式是在case文件的setup中调用gettoken方法拿到token,存入一个变量,然后在每个case中使用这个token变量 但后面 ...

  9. pytest.fixture详解

    scope分为session.package.module.class.function级别,其中function也是包含测试类的测试方法的,package指在当前contest.py的package ...

  10. pytest、pytest.mark和pytest.fixture的用法

    1.pytest的格式:模块名用以test开头或者结尾,类名为Test开头,函数名以test开头,同时里面不能含构造函数(__init__),如果有继承,建议用setup和teardown. 2.运行 ...

最新文章

  1. error response from daemon_Scrapy 框架-模拟登录-Request、Response
  2. HttpSecurity初步理解
  3. H3C S1526交换机端口镜像配置
  4. [python]网络编程基础学习笔记(一)客户/服务器网络介绍
  5. PyTorch 1.0 中文官方教程:使用字符级别特征的 RNN 网络进行姓氏分类
  6. 使用hessian+protocol buffer+easyUI综合案例--登陆
  7. Spring源码之bean的加载(四)获取单例
  8. JavaScript对滚动条的操作
  9. servletconfigservletcontext
  10. ArcScan矢量化
  11. 本地音乐播放器Demo
  12. 计算机word文档基本操作,Word常用基本操作
  13. 【PS】如何使用Photoshop调整图片指定部分的大小
  14. 一位阿里p9的自述——关于年薪和资产
  15. 涨价不再“嘘声”一片,“爱优腾”集体“想开了”?
  16. 空指针异常(NullPointerException)
  17. RSS/RPS/RFS究竟是个什么东西
  18. calender 源码 CSS+JS
  19. python狗品种识别_使用python+keras来识别狗的品种
  20. 图灵深度学习工作站介绍

热门文章

  1. 正版Xshell 7免费使用,你还在找破解版吗
  2. 最新版抖音抓包:免费送抖音抓包模块
  3. 极客漫画-linux合集(三)
  4. python开发的程序中以电子表格显示数据_使用 Python 读取电子表格中的数据实例详解...
  5. .NET Framework 3.5安装教程
  6. ListView 实现斑马线颜色间隔效果(ListViewItem颜色间隔显示)
  7. Docker三剑客之swarm
  8. 使用NPOI实现在Excel第一行插入文字
  9. 5种方法解除开机密码
  10. c语言中式等号右边赋予左边吗,中式设计文字资料大全.doc