Using pytest with an existing test suite¶

pytest可以运行既有unittest或者nose写成的测试用例。建议把受测代码以及测试用例安装到在虚拟环境中,这样一来可以避免修改sys.path,二来可以避免多次安装:

cd <repository>
pip install -e .  # Environment dependent alternatives include
                  # 'python setup.py develop' and 'conda develop'

另一个选择是使用tox

The writing and reporting of assertions in tests¶

pytest允许直接使用python的assert来进行断言。如果要测试异常的话,可以:

import pytest


def test_zero_division():
    with pytest.raises(ZeroDivisionError):
        1 / 0

如果需要访问异常的回溯信息,可以:

def test_recursion_depth():
    with pytest.raises(RuntimeError) as excinfo:
        f()
    assert "maximum recursion" in str(excinfo.value)

exeinfo是ExceptionInfo 的实例。

和unittest的TestCase.assertRaisesRegexp方法相同,可以使用match来匹配异常:

def test_match():
    with pytest.raises(ValueError, match=r".* 123 .*"):
        myfunc()

match是通过re.search实现的,所以上面的例子也可以写成 match='123'

pytest.raise有一个变体:pytest.raises(ExpectedException, func, *args, **kwargs),往里传一个函数,让这个函数来消耗args和kwargs。

在pytest.mark.xfail上也可以指定一个异常:

@pytest.mark.xfail(raises=IndexError)
def test_f():
    f()

针对warning可以使用pytest.warns

对于比较,pytest可以产生比较丰富的上下文信息。如果不适用,则可以自定义pytest_assertrepr_compare来添加额外的信息。一个例子:

# content of conftest.py
from test_foocompare import Foo


def pytest_assertrepr_compare(op, left, right):
    if isinstance(left, Foo) and isinstance(right, Foo) and op == "==":
        return [
            "Comparing Foo instances:",
            "   vals: {} != {}".format(left.val, right.val),
        ]

pytest是通过对assert语句进行改写来增加额外的信息,pytest只会改写测试用例中的断言,不会触及其他代码中的assert。可以通过调用register_assert_rewrite 来对指定模块进行assert改写。更多参考Behind the scenes of pytest’s new assertion rewriting.

pytest会被改写后的模块写回硬盘以便进行缓存。可以通过sys.dont_write_bytecode = True来取消这个行为。

pytest通过import钩子作为改写的时机。有时候需要禁止改写:

  • 在模块的docstring中加入PYTEST_DONT_REWRITE
  • 在命令行指定–assert=plain (废弃–no-assert、–nomagic以及–assert=reinterp)

(本篇完)