pytest:如何在测试中编写和报告断言

断言assert声明

pytest允许我们使用python的标准断言来测试期望值和预期值。例如:

def dunc():
    return 3

def test_dunc():
    assert dunc() != 3, "value was odd, should be even"

断言dunc函数返回的某个值。如果此断言失败,你就看下函数调用的返回值:

============================= test session starts =============================
collecting ... collected 1 item

assert_test.py::test_dunc FAILED                                         [100%]
assert_test.py:12 (test_dunc)
3 != 3

Expected :3
Actual   :3
<click to see difference>

def test_dunc():
>       assert dunc() != 3
E       assert 3 != 3
E        +  where 3 = dunc()

assert_test.py:14: AssertionError

============================== 1 failed in 0.08s ==============================

Process finished with exit code 1
</click>

如果我们使用这样的断言指定消息:

def dunc():
    return 3

def test_dunc():
    value = 4
    assert dunc() == value, f"value was {dunc()}, should be {value}"

这时候的结果返回的就是我们指定的信息。就像下面这样:

============================= test session starts =============================
collecting ... collected 1 item

assert_test.py::test_dunc FAILED                                         [100%]
assert_test.py:12 (test_dunc)
3 != 4

Expected :4
Actual   :3
<click to see difference>

def test_dunc():
        value = 4
>       assert dunc() == value, f"value was {dunc()}, should be {value}"
E       AssertionError: value was 3, should be 4
E       assert 3 == 4
E        +  where 3 = dunc()

assert_test.py:15: AssertionError

============================== 1 failed in 0.08s ==============================

Process finished with exit code 1
</click>

关于预期异常的断言

def test_exception_assertion():
    assert 1 == 2

============================= test session starts =============================
collecting ... collected 1 item

exception_assertion_test.py::test_exception_assertion FAILED             [100%]
exception_assertion_test.py:11 (test_exception_assertion)
1 != 2

Expected :2
Actual   :1
<click to see difference>

def test_exception_assertion():
>           assert 1 == 2
E           assert 1 == 2

exception_assertion_test.py:13: AssertionError

============================== 1 failed in 0.10s ==============================
</click>

上述这个例子我们断言1==2;但是实际是 1 != 2 所以会抛出断言异常,对于预期会抛出的异常,我们可以使用pytest.raises()帮助我们解决预期引发的异常断言。就像下面这样:

import pytest

def test_exception_assertion():
    with pytest.raises(AssertionError):
        assert 1 == 2

============================= test session starts =============================
collecting ... collected 1 item

exception_assertion_test.py::test_exception_assertion PASSED             [100%]

============================== 1 passed in 0.01s ==============================

Process finished with exit code 0

因为我们做了已知异常的捕获,所以再运行就不会再抛出异常了。如果您需要访问可以使用的实际异常信息:

import pytest

def func():
    func()

def test_raises():
    with pytest.raises(RuntimeError) as excinfo:
        func()

    print(excinfo.type)
    print(excinfo.value)
    print(excinfo.traceback)

    assert "maximum recursion" in str(excinfo.value)

============================= test session starts ==============================
collecting ... collected 1 item

raises_test.py::test_raises PASSED                                       [100%]
<class 'recursionerror'>
maximum recursion depth exceeded
[<tracebackentry users lifeng python-projects python-code pytestproject cases raises_test.py:18>, ...]
============================== 1 passed in 0.05s ===============================

Process finished with exit code 0
</tracebackentry></class>

excinfo是一个异常捕获的实例,它是引发的实际异常的包装器。主要属性 .type、.value和 .traceback 。.type是获取异常的类,.value是获取异常类抛出的message信息,.traceback是获取错误的回溯信息。

您也可以利用正则表达式匹配异常的字符串表示形式,传递给match关键字参数,然后match传递给上下文管理器:

def func():
    raise ValueError("ValueError message is error !")

def test_raises_match():
    with pytest.raises(ValueError, match=r".* message.*"):
        func()

============================= test session starts ==============================
collecting ... collected 1 item

raises_test.py::test_raises_match PASSED                                 [100%]

============================== 1 passed in 0.01s ===============================

Process finished with exit code 0

正表达式中的.是匹配除换行符(\n、\r)之外的任何单个字符,* 是匹配前面的子表达式零次或多次。因为func抛出的错误中包含message字符,正则刚好也匹配到异常信息,属于我们预期捕获的异常,所以运行成功。

也可以为 指定一个 ” raises ” 参数 pytest.mark.xfail,它以一种更具体的方式检查测试是否失败,而不仅仅是引发任何异常:

import pytest

def func():
    raise ValueError("ValueError message is error !")

@pytest.mark.xfail(raises=ValueError, reason="&#x503C;&#x9519;&#x8BEF;&#xFF01;")
def test_raises_match():
    func()

============================= test session starts ==============================
collecting ... collected 1 item

raises_test.py::test_raises_match XFAIL (&#x503C;&#x9519;&#x8BEF;&#xFF01;)                       [100%]
@pytest.mark.xfail(raises=ValueError, reason="&#x503C;&#x9519;&#x8BEF;&#xFF01;")
    def test_raises_match():
>       func()

raises_test.py:28:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

    def func():
>       raise ValueError("ValueError message is error !")
E       ValueError: ValueError message is error !

raises_test.py:23: ValueError

============================== 1 xfailed in 0.11s ==============================

Process finished with exit code 0

pytest.raises() 和 @pytest.mark.xfail 的比较:

pytest.raises() 对于您正在测试自己的代码故意引发的异常的情况使用可能会更好。

@pytest.mark.xfail 检查功能,更适合记录未修复的错误(测试描述”应该”发生什么)或依赖项中的错误。

为失败的断言定义自定义的解释

首先,我们要先写一个断言类。就像下面这样:

class AssertTest:
    def __init__(self, val):
        self.val = val

    def __eq__(self, other):
        return self.val == other.val

利用__eq__方法来进行判断,一个是self,另一个是other,也就是用自身的属性和other对象的属性分别进行比较

断言类写好以后,我们要在用例文件夹中新建一个conftest.py文件,该文件主要是定义一些前置后置夹具,以及钩子函数的二次扩充编写。现在我们在文件中重写钩子来实现断言,就像下面这样:

文档提供的钩子函数源码是这样的:

def  pytest_assertrepr_compare (
    config :  "Config" ,  op :  str ,  left :  object ,  right :  object
)  ->  Optional [ List [ str ]]:
    """&#x8FD4;&#x56DE;&#x5931;&#x8D25;&#x65AD;&#x8A00;&#x8868;&#x8FBE;&#x5F0F;&#x4E2D;&#x6BD4;&#x8F83;&#x7684;&#x89E3;&#x91CA;&#x3002;

    &#x8FD4;&#x56DE; None &#x8868;&#x793A;&#x6CA1;&#x6709;&#x81EA;&#x5B9A;&#x4E49;&#x89E3;&#x91CA;&#xFF0C;&#x5426;&#x5219;&#x8FD4;&#x56DE;&#x5B57;&#x7B26;&#x4E32;&#x5217;&#x8868;
    &#x3002;&#x5B57;&#x7B26;&#x4E32;&#x5C06;&#x7531;&#x6362;&#x884C;&#x7B26;&#x8FDE;&#x63A5;&#xFF0C;&#x4F46;&#x4EFB;&#x4F55;&#x6362;&#x884C;&#x7B26;
    *in* a string &#x90FD;&#x5C06;&#x88AB;&#x8F6C;&#x4E49;&#x3002;&#x8BF7;&#x6CE8;&#x610F;&#xFF0C;&#x9664;&#x4E86;&#x7B2C;&#x4E00;&#x884C;&#x4E4B;&#x5916;&#x7684;&#x6240;&#x6709;&#x884C;&#x90FD;&#x4F1A;
    &#x7565;&#x5FAE;&#x7F29;&#x8FDB;&#xFF0C;&#x76EE;&#x7684;&#x662F;&#x8BA9;&#x7B2C;&#x4E00;&#x884C;&#x4F5C;&#x4E3A;&#x6458;&#x8981;&#x3002;

    :param pytest.Config config&#xFF1A;pytest &#x914D;&#x7F6E;&#x5BF9;&#x8C61;&#x3002;
"""

conftest.py文件中重新定义钩子函数:

from common.assertrepr import AssertTest

def pytest_assertrepr_compare(op, left, right):
    if isinstance(left, AssertTest) and isinstance(right, AssertTest) and op == "==":
        return [
            "&#x6BD4;&#x8F83; AssertTest &#x7684;&#x5B9E;&#x4F8B;:",
            "   vals: {} != {}".format(left.val, right.val),
        ]

isinstance是判断传的类型是否和预期的类型一致,如果都不满足if条件,则抛出return 。left 表示操作符左侧被比较的对象,right 表示操作符右侧被比较的对象,op 表示断言的操作符。

上述自定义的解释断言编写好以后,开始编写测试用例。就像这样:

from common.assertrepr import AssertTest

def test_assertrepr_compare():
    a = AssertTest(1)
    b = AssertTest(2)
    assert a == b

============================= test session starts =============================
collecting ... collected 1 item

assertrepr_compare_test.py::test_assertrepr_compare FAILED               [100%]

assertrepr_compare_test.py:11 (test_assertrepr_compare)
<common.assertrepr.asserttest object at 0x0000016ef1385e10> != <common.assertrepr.asserttest object at 0x0000016ef1385d50>

Expected :<common.assertrepr.asserttest object at 0x0000016ef1385d50>
Actual   :<common.assertrepr.asserttest object at 0x0000016ef1385e10>
<click to see difference>

def test_assertrepr_compare():
        a = AssertTest(1)
        b = AssertTest(2)
>       assert a == b
E       assert &#x6BD4;&#x8F83; AssertTest &#x7684;&#x5B9E;&#x4F8B;:
E            vals: 1 != 2

assertrepr_compare_test.py:15: AssertionError

============================== 1 failed in 0.08s ==============================

Process finished with exit code 1
</click></common.assertrepr.asserttest></common.assertrepr.asserttest></common.assertrepr.asserttest></common.assertrepr.asserttest>

今天先聊到这里吧,以上总结或许能帮助到你,或许帮助不到你,但还是希望能帮助到你

最后: 可以在公众号: 伤心的辣条 ! 自行领取一份216页软件测试工程师面试宝典文档资料【免费的】。以及相对应的视频学习教程免费分享!,其中包括了有基础知识、Linux必备、Shell、互联网程序原理、Mysql数据库、抓包工具专题、接口测试工具、测试进阶-Python编程、Web自动化测试、APP自动化测试、接口自动化测试、测试高级持续集成、测试架构开发测试框架、性能测试、安全测试等。

现在我邀请你进入我们的软件测试学习交流群: 746506216】,备注”入群”, 大家可以一起探讨交流软件测试,共同学习软件测试技术、面试等软件测试方方面面,还会有免费直播课,收获更多测试技巧, &#x6211;&#x4EEC;&#x4E00;&#x8D77;&#x8FDB;&#x9636;Python&#x81EA;&#x52A8;&#x5316;&#x6D4B;&#x8BD5;/&#x6D4B;&#x8BD5;&#x5F00;&#x53D1;&#xFF0C;&#x8D70;&#x5411;&#x9AD8;&#x85AA;&#x4E4B;&#x8DEF;&#x3002;

喜欢软件测试的小伙伴们,如果我的博客对你有帮助、如果你喜欢我的博客内容,请 “点赞” “评论” “收藏” 一 键三连哦!

软件测试工程师自学教程:

这才是2022最精细的自动化测试自学教程,我把它刷了无数遍才上岸字节跳动,做到涨薪20K【值得自学软件测试的人刷】

接口性能测试 — 软件测试人必会618实战场景分析

软件测试工程师月薪2W以上薪资必学技能 — Python接口自动化框架封装.

美团面试真题_高级测试25K岗位面试 — 软件测试人都应该看看

测试开发之全面剖析自动化测试平台 — 软件测试人的必经之路

软件测试必会_Jmeter大厂实战 — 仅6步可实现接口自动化测试

Jmeter实战讲解案例 — 软件测试人必会

pytest:如何在测试中编写和报告断言

pytest:如何在测试中编写和报告断言

Original: https://blog.csdn.net/m0_67695717/article/details/126247766
Author: 测试界的飘柔
Title: pytest:如何在测试中编写和报告断言

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/774164/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球