阅读 50

pytest之fixture简介

fixture介绍

fixture是在测试函数运行前后,由pytest执行的外壳函数。fixture中的代码可以定制,满足多变的测试需求,包括定义传入测试中的数据集、配置测试前系统的初始状态、为批量测试提供数据源,等等。

import pytest

@pytest.fixture()
def some_data():
    return 42

def test_some_data(some_data):
    assert some_data == 42
复制代码

@pytest.fixture()装饰器用于声明函数是一个fixture。如果测试函数的参数列表中包含fixture名,那么pytest会检测到,并在测试函数运行之前执行该fixture。fixture可以完成任务,也可以返回数据给测试函数。

通过conftest.py共享fixture

如果你希望多个测试文件共享fixture,可以在某个公共目录下新建一个conftest.py文件,将fixture放在其中。conftest.py被pytest视作一个本地插件库。可以把tests/conftest.py看成是一个供tests目录下所有测试使用的fixture仓库。

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

import pytest
import tasks
from tasks import Task

@pytest.fixture()
def tasks_db(tmpdir):
    """connect to db defore 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函数会在测试函数之前运行,但如果fixture函数包含yield,那么系统会在yield处停止,转而运行测试函数,等测试函数执行完毕后再回到fixture,继续执行yield后面的代码。因此,可以将yield之前的代码视为配置(setup)过程,将yield之后的代码视为清理(teardown)过程。无论测试过程中发生了什么,yield之后的代码都会被执行。

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

使用--setup-show选项可以看到测试过程中执行的是什么,以及执行的先后顺序。 fixture名称前面的F和S代表的是fixture的作用范围,F代表函数级别的作用范围,S代表会话级别的作用范围。

(ly_venv) liuyan@Lydemacpro func % pytest --setup-show test_add.py -k valid_id
============================= test session starts ==============================
platform darwin -- Python 3.6.4, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: /Users/liuyan/PycharmProjects/code/ch3/a/tasks_proj/tests, configfile: pytest.ini
plugins: allure-pytest-2.8.34, html-3.1.1, metadata-1.11.0
collected 3 items / 2 deselected / 1 selected                                  

test_add.py 
SETUP    S tmp_path_factory
        SETUP    F tmp_path (fixtures used: tmp_path_factory)
        SETUP    F tmpdir (fixtures used: tmp_path)
        SETUP    F tasks_db (fixtures used: tmpdir)
        func/test_add.py::test_add_returns_valid_id (fixtures used: request, tasks_db, tmp_path, tmp_path_factory, tmpdir).
        TEARDOWN F tasks_db
        TEARDOWN F tmpdir
        TEARDOWN F tmp_path
TEARDOWN S tmp_path_factory
复制代码

使用fixture传递测试数据

fixture非常适合存放测试数据,并且它可以返回任何数据。

import pytest

@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
复制代码

假设assert异常(或任何类型的异常)就发生在fixture,则报告会用ERROR而不是FAIL。如果测试结果为FAIL,用户就知道失败是发生在核心测试函数内,而不是发生在测试依赖的fixture。可以使用多个fixture。

使用fixture的优势在于:用户在编写测试函数时可以只考虑核心测试逻辑,而不需要考虑测试前的准备工作

指定fixture作用范围

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

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

使用usefixtures指定fixture

可以用@pytest.mark.usefixtures('fixture1','fixture2')标记测试函数或类。使用usefixtures,需要在参数列表中指定一个或多个fixture字符串。这对测试函数来讲意义不大,但非常适合测试类。

@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的返回值。

为fixture重命名

pytest允许使用@pytest.fixture()的name参数对fixture重命名。

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
复制代码
文章分类
代码人生
文章标签