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

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

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

关于预期异常的断言

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 ==============================

上述这个例子我们断言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

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="值错误!")
def test_raises_match():
    func()


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

raises_test.py::test_raises_match XFAIL (值错误!)                       [100%]
@pytest.mark.xfail(raises=ValueError, reason="值错误!")
    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 ]]: 
    """返回失败断言表达式中比较的解释。

    返回 None 表示没有自定义解释,否则返回字符串列表
    。字符串将由换行符连接,但任何换行符
    *in* a string 都将被转义。请注意,除了第一行之外的所有行都会
    略微缩进,目的是让第一行作为摘要。

    :param pytest.Config config:pytest 配置对象。
    """

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 [
            "比较 AssertTest 的实例:",
            "   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 比较 AssertTest 的实例:
E            vals: 1 != 2

assertrepr_compare_test.py:15: AssertionError


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

Process finished with exit code 1

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

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

现在我邀请你进入我们的软件测试学习交流群:746506216】,备注“入群”, 大家可以一起探讨交流软件测试,共同学习软件测试技术、面试等软件测试方方面面,还会有免费直播课,收获更多测试技巧,我们一起进阶Python自动化测试/测试开发,走向高薪之路。

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

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

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

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

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

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

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

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

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

在这里插入图片描述

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/m0_67695717/article/details/126247766