[翻译]pytest测试框架(三):断言编写和报告

勿忘初心2018-10-19 18:40


此文已由作者吴琪惠授权网易云社区发布。

欢迎访问网易云社区,了解更多网易技术产品运营经验。



使用assert语言进行断言

pytest允许您使用标准的python assert方法来验证期望值,例如:

# content of test_assert1.pydef f():
    return 3def test_function():
    assert f() == 4

断言函数会返回某个值,如果断言失败,会看到函数的返回值:

$ pytest test_assert1.py
======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 items

test_assert1.py F

======= FAILURES ========
_______ test_function ________  
 def test_function():>      
  assert f() == 4E      
   assert 3 == 4E       
    +  where 3 = f()

test_assert1.py:5: AssertionError
======= 1 failed in 0.12 seconds ========

pytest支持最常见的表达式(包括调用、熟悉、比较、二进制、一元操作符),这允许你不需要使用python惯用的代码结构,且不会丢失缺省信息。

但是,如果你指定一个断言如下:

assert a % 2 == 0, "value was odd, should be even"

那么,因为找不到缺省断言,这句会直接出现在traceback。


异常断言

使用pytest.raises进行异常断言:

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

如果你需要访问时间的异常信息;

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

excinfo是一个ExceptionInfo实例,是由实际的异常抛出之后包装的。需要关心的属性有 .type, .value 和 .traceback


3.0版本的改动:

你可以使用meassage参数来自定义一个失败消息:

>>> with raises(ZeroDivisionError, message="Expecting ZeroDivisionError"):...    pass... Failed: Expecting ZeroDivisionError

若你想要python2.4版本也可以适用,还可以使用其他两个方式:

pytest.raises(ExpectedException, func, *args, **kwargs)pytest.raises(ExpectedException, "func(*args, **kwargs)")

上面两个执行含有args和kwargs的特殊方法,并且断言给定的ExpectedException。报告将会提供有用的错误输出信息,比如 no exceptin 或者 wrong exception

注意,我们也可以给pytest.mark.xfail指定 raises 参数,这样用例失败之后会给出一个更加自定义的错误异常:

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

针对你自己故意抛出的测试异常,使用pytest.raises的方式更好,而使用@pytest.mark.xfail 类似的检查功能可能是用于更好的记录不固定的bug

如果你想要测试一个正则表达式匹配的字符串异常,有点类似unittest里的TestCase.assertRaiseRegexp方法,可以使用ExceptionInfo.match方法:

import pytestdef myfunc():
    raise ValueError("Exception 123 raised")def test_match():
    with pytest.raises(ValueError) as excinfo:
        myfunc()
    excinfo.match(r'.* 123 .*')

match方法的正则表达式参数是匹配python里的re.search方法,所以,在上面的例子中 exeinfo.match('123')可以正常的被匹配到。


警告断言(2.8版本)

使用pytest.warns

比较上下文敏感词

# content of test_assert2.pydef test_set_comparison():
    set1 = set("1308")
    set2 = set("8035")
    assert set1 == set2

运行:

$ pytest test_assert2.py
======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 items

test_assert2.py F

======= FAILURES ========
_______ test_set_comparison ________    def test_set_comparison():
        set1 = set("1308")
        set2 = set("8035")
>       assert set1 == set2
E       assert {'0', '1', '3', '8'} == {'0', '3', '5', '8'}
E         Extra items in the left set:
E         '1'E         Extra items in the right set:
E         '5'E         Use -v to get the full diff

test_assert2.py:5: AssertionError
======= 1 failed in 0.12 seconds ========

几个用例的特别比较:

1.比较长字符串:显示了diff

2.标记长序列:第一个失败的index

3.比价字典:不相等的条目


自定义断言

regexp匹配方法的参数匹配与re.search函数。所以在上面的例子中excinfo.match(“123”)工作。


网易云免费体验馆,0成本体验20+款云产品! 

更多网易技术、产品、运营经验分享请点击


相关文章:
【推荐】 浅谈容器监控和网易云计算基础服务实践
【推荐】 Android标题栏(1)