欢迎大家搜索关注微信公众号“米兔软件测试”,测试开发工程师技术修炼小站,持续分享测试干货。
上一小节中,我们详细梳理了pytest常见的执行命令,本小节我们来讲一下pytest的assert断言方法。
首先我们讲解一下测试中的断言是什么意思?定义如下
在软件测试中,断言(Assertion)是一种用于检查程序或系统在执行某些操作或运行某些代码后是否符合预期行为的技术手段。在测试中,断言通常用于验证代码的正确性,确保代码在满足特定条件时会产生预期的结果。底层原理主要是通过比较实际结果与预期结果来判断测试是否通过。断言是测试用例的关键组成部分,它可以帮助测试人员快速、准确地确定问题所在,缩短测试周期,提高软件质量。
在自动化测试代码中,断言通常以一种特殊的语法形式出现,例如使用 assert() 函数或类似 PHPUnit 这样的测试框架提供的断言方法。这些断言方法可以接受一个或多个参数,用于指定要检查的条件和期望的结果。当断言失败时,它们通常会抛出一个异常或者返回一个错误,以表明程序或系统在执行时出现了错误或异常行为。
pytest测试框架中的断言用的python原生自带的关键字assert,并且它对assert语句进行了优化,当发生断言失败时,错误信息更加丰富,方便测试时快速定位问题原因。
python中assert关键字的语法如下
assert expression1 ["," expression2]
expression1往往是一个条件表达式,如果条件表达式为True,则什么也不做,相当于执行了 pass 语句
常见的expression1表达式,有如下几种
--assert x:断言x为真
---assert not x:断言x不为真
---assert x in y:断言y包含x
---assert x not in y:断言y不包含x
---assert x==y:断言x等于y
---assert x !=y:断言x不等于y
---assert x >y:断言x大于y
---assert x ---assert x >=y:断言x大于等于y 如果条件表达式为False,便会抛出异常 AssertionError,并返回具体的错误信息expression2。 在自动化测试用例中,最常用的断言是相等断言,就是断言预期结果和实际结果是一致的。通常我们断言的预期结果和实际结果的数据类型是数字、字符串、元组、字典、列表和对象。 pytest通过assert和==能够完美支持对这些数据类型的相等断言。下面来介绍几种常见的数据类型的断言操作。 除了支持对代码正常运行的结果断言之外,Pytest也能够对Exception和Warnning进行断言,来断定某种条件下,一定会出现某种异常或者警告。在功能测试和集成测试中,这两类断言用的不多,这里简单介绍一下。 对于异常的断言,pytest的语法是:with pytest.raises(异常类型) 这个测试用例断言运算表达式1除以0会产生ZeroDivisionError异常。除了对异常类型进行断言,还可以对异常信息进行断言,比如 对于Warnning的断言,您可以使用 pytest.warns 检查代码是否引发了特定警告。其实与Exception的断言的用法基本一致。这里就不介绍了。 通过前面的介绍,感觉Pytest的assert挺完美了,又简单又清晰。 但是在实际的测试工作中,还会遇到一些实际问题,比如在断言时,最好【自动】添加一些日志,避免我们在测试代码中手动加入日志。还有,最好能将断言的信息,【自动】集成到一些测试报告中,比如Pycharm+pytest+allure打造高逼格的测试报告。这样就能避免在每一个测试脚本中手动写很多重复的代码,从而让我们将更多的时间和精力放到编写测试用例上。 有了这样的想法,接下来看看如何实现。 pytest中提供了一个Hook函数pytest_assertrepr_compare,这个函数会在测试脚本的assert语句执行时被调用。因此,可以实现这个函数,在函数中添加写日志和集成allure测试报告代码。 完整的代码如下所示: 通过inspect获取调用栈信息,从中得到测试脚本中assert语句中op操作符两边的字符串名称,在日志和测试报告中会用到。 接着执行assertrepr_compare输出错误详细信息,这些信息就是在执行断言失败时的输出内容,pytest_assertrepr_compare函数没有对其做任何修改。接着添加了debug日志输出和allure测试报告的内容,最后再将assert的错误信息返回给调用处。 实现了这个函数后,测试脚本不需要做任何修改,依然是直接使用assert进行断言。但是能够自动记录日志和生成allure测试报告了。 如果不想要pytest中的assert的效果,而是希望保持python原生的assert效果,只需要在执行测试是指定一个选项: 这样所有测试用例中的assert都变成了python原生的assert效果了,如果只想某一个模块保持python原生的assert效果,那么就在对应模块的docstring中添加PYTEST_DONT_REWRITE字符串就好了,也就是在py文件的最上面添加类似下面的docstring内容: 不过,我想应该没有人会这么干,因为pytest的assert还是更好用一些。 最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走,希望可以帮助到大家!# 断言1+1是否等于2,断言成功
def test_assert_success():
assert 1+1 == 2
Test Results:
Launching pytest with arguments requests_demo.py::test_assert_success --no-header --no-summary -q in D:\PycharmProjects\Source_Code\pytest_demo
============================= test session starts =============================
collecting ... collected 1 item
requests_demo.py::test_assert_success PASSED [100%]
============================== 1 passed in 0.02s ==============================
Process finished with exit code 0
# 断言1+1是否等于3,断言失败,抛出错误提示消息
def test_assert_success():
assert 1+1 == 3,"哈哈哈,1+1是不等于3"
Test Results:
aunching pytest with arguments requests_demo.py::test_assert_success --no-header --no-summary -q in D:\PycharmProjects\Source_Code\pytest_demo
============================= test session starts =============================
collecting ... collected 1 item
requests_demo.py::test_assert_success FAILED [100%]
requests_demo.py:1 (test_assert_success)
2 != 3
Expected :3
Actual :2
可以看下面的这个例子:def test_zero_division():
with pytest.raises(ZeroDivisionError):
1 / 0
def test_zero_division():
with pytest.raises(ZeroDivisionError) as excinfo:
1 / 0
assert 'division by zero' in str(excinfo.value)
# content of conftest.py
def pytest_assertrepr_compare(config, op, left, right):
left_name, right_name = inspect.stack()[7].code_context[0].lstrip().lstrip('assert').rstrip('
').split(op)
pytest_output = assertrepr_compare(config, op, left, right)
logging.debug("{0} is\n {1}".format(left_name, left))
logging.debug("{0} is\n {1}".format(right_name, right))
with allure.step("校验结果"):
allure.attach(str(left), left_name)
allure.attach(str(right), right_name)
return pytest_output
--assert=plain
"""
Disable rewriting for a specific module by adding the string:
PYTEST_DONT_REWRITE
"""