pytest之assert断⾔的具体使⽤
背景
本⽂总结使⽤pytest编写⾃动化测试时常⽤的assert断⾔。
说明
本⽂将从以下⼏点做总结:
1. 为测试结果作断⾔
2. 为断⾔不通过的结果添加说明信息
3. 为预期异常作断⾔
4. 为失败断⾔⾃定义说明信息
为测试结果作断⾔
在断⾔⽅⾯,pytest框架⽐其他类似的框架(⽐如unittest)更加简洁,易⽤,我想这是我选择pytest作为⾃动化测试框架之⼀的原因之⼀。
pytest的assert断⾔关键字⽀持使⽤python内置的assert表达式。可以理解为pytest的断⾔就是直接使⽤python⾃带的assert关键字。
python assert的概念:
Python assert(断⾔)⽤于判断⼀个表达式,在表达式条件为 false 的时候触发异常。
我们可以在在assert后⾯添加任何符合python标准的表达式,如果表达式的值通过bool转换后等于False,则意味着断⾔结果为失败。
以下举例常⽤的表达式:
# ./test_case/test_func.py
import pytest
from func import *
class TestFunc:
def test_add_by_class(self):
assert add(2,3) == 5
def test_add_by_func_aaa():
assert 'a' in 'abc'
assert 'a' not in 'bbc'
something = True
assert something
something = False
assert not something
assert 1==1
assert 1!=2
assert 'a' is 'a'
assert 'a' is not 'b'
assert 1 < 2
assert 2 > 1
assert 1 <= 1
assert 1 >= 1
assert add(3,3) == 6
'''
# 以上全是合法的表达式且表达式的值都为True,所以测试结果为通过
============================= test session starts =============================
platform win32 -- Python 3.7.0, pytest-5.3.4, py-1.8.1, pluggy-0.13.1 -- D:\Python3.
cachedir: .pytest_cache
rootdir: D:\Python3.7\project\pytest, inifile: pytest.ini
plugins: allure-pytest-2.8.9, rerunfailures-8.0
collecting ... collected 2 items
test_case/test_func.py::TestFunc::test_add_by_class PASSED              [ 50%]
test_case/test_func.py::test_add_by_func_aaa PASSED                      [100%]
============================== 2 passed in 0.06s ==============================
[Finished in 1.8s]
'''
为断⾔不通过的结果添加说明信息
在编写测试时,为了提⾼易⽤性,我们想知道断⾔失败时的⼀些关于失败的原因等说明信息,assert也能满⾜该功能。
请看⽰例:
# ./test_case/test_func.py
import pytest
from func import *
class TestFunc:
def test_add_by_class(self):
assert add(2,3) == 5
def test_add_by_func_aaa():
assert add(3,3) == 5, "3+3应该等于6"
'''
============================= test session starts =============================
platform win32 -- Python 3.7.0, pytest-5.3.4, py-1.8.1, pluggy-0.13.1 -- D:\Python3.
cachedir: .pytest_cache
rootdir: D:\Python3.7\project\pytest, inifile: pytest.ini
plugins: allure-pytest-2.8.9, rerunfailures-8.0
collecting ... collected 2 items
test_case/test_func.py::TestFunc::test_add_by_class PASSED              [ 50%]
test_case/test_func.py::test_add_by_func_aaa FAILED                      [100%]
================================== FAILURES ===================================
____________________________ test_add_by_func_aaa _____________________________
def test_add_by_func_aaa():
>    assert add(3,3) == 5, "3+3应该等于6"
E    AssertionError: 3+3应该等于6
E    assert 6 == 5
E      -6
E      +5
test_case\test_func.py:14: AssertionError
========================= 1 failed, 1 passed in 0.09s =========================
[Finished in 1.4s]
python正则表达式爬虫'''
为预期异常作断⾔
在某些测试⽤例中,⽐如异常测试⽤例,测试的结果必然是失败并应该爆出异常的。这时候⾃动化测试⽤例的期望结果就是该异常。如果期望结果等于该异常,那么测试⽤例执⾏通过,否则⽤例结果为失败。pytest提供为为预期异常作断⾔的⽅法:pytest.raises()。⼀般结合with上下⽂管理器使⽤。
使⽤⽰例:
# ./func.py
def add(a,b):
if isinstance(a,int) and isinstance(b,int):
return a+b
else:
raise NameError('数据类型错误')
# ./test_case/test_func.py
import pytest
from func import *
class TestFunc:
# 正常测试⽤例
assert add(2,3) == 5
# 异常测试⽤例,期望结果为爆出TypeError异常
def test_add_by_func_aaa():
with pytest.raises(TypeError):
add('3',4)
# ./run_test.py
import pytest
if __name__ == '__main__':
pytest.main(['-v'])
'''
============================= test session starts ============================= platfo
rm win32 -- Python 3.7.0, pytest-5.3.4, py-1.8.1, pluggy-0.13.1 -- D:\Python3. cachedir: .pytest_cache
rootdir: D:\Python3.7\project\pytest, inifile: pytest.ini
plugins: allure-pytest-2.8.9, rerunfailures-8.0
collecting ... collected 2 items
test_case/test_func.py::TestFunc::test_add_by_class PASSED              [ 50%]
test_case/test_func.py::test_add_by_func_aaa PASSED                      [100%]
============================== 2 passed in 0.06s ============================== [Finished in 1.4s]
'''
接下来看看没有爆出预期异常的⽰例:
# ./func.py
def add(a,b):
# 指定异常
raise NameError("天降异常")
if isinstance(a,int) and isinstance(b,int):
return a+b
else:
raise NameError('数据类型错误')
# ./test_case/test_func.py
import pytest
from func import *
'''
class TestFunc:
# 正常测试⽤例
def test_add_by_class(self):
assert add(2,3) == 5
'''
# 异常测试⽤例,期望结果为爆出TypeError异常
def test_add_by_func_aaa():
with pytest.raises(TypeError):
add('3',4)
# ./run_test.py
import pytest
if __name__ == '__main__':
pytest.main(['-v'])
'''
============================= test session starts ============================= platform win32 -- Python 3.7.0, pytest-5.3.4, py-1.8.1, pluggy-0.13.1 -- D:\Python3. cachedir: .pytest_cache
rootdir: D:\Python3.7\project\pytest, inifile: pytest.ini
plugins: allure-pytest-2.8.9, rerunfailures-8.0
collecting ... collected 1 item
test_case/test_func.py::test_add_by_func_aaa FAILED                      [100%]
================================== FAILURES =================================== ____________________________ test_add_by_func_aaa _____________________________
with pytest.raises(TypeError):
>    add('3',4)
test_case\test_func.py:14:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
a = '3',
b = 4
def add(a,b):
# 指定异常
>    raise NameError("天降异常")
E    NameError: 天降异常
func.py:4: NameError
============================== 1 failed in 0.09s ==============================
[Finished in 1.4s]
'''
判定⽤例执⾏结果为失败。
上⾯我们只是断⾔了异常的类型。但有的时候我们想更进⼀步断⾔异常的说明信息,pytest也可以做到。with pytest.raises()执⾏结束后会⽣成⼀个ExceptionInfo的实例对象。该对象包含type , value, traceback属性。value属性就是我们需要的异常说明信息。
见⽰例:
# ./func.py
def add(a,b):
if isinstance(a,int) and isinstance(b,int):
return a+b
else:
raise TypeError('数据类型错误')
# ./test_case/test_func.py
import pytest
from func import *
class TestFunc:
# 正常测试⽤例
def test_add_by_class(self):
assert add(2,3) == 5
# 异常测试⽤例,期望结果为爆出TypeError异常
def test_add_by_func_aaa():
with pytest.raises(TypeError) as E:
add('3',4)
pe)
print(E.value)
aceback)
# 加⼊该不通过断⾔为了查看stdout
assert 1 == 2
# ./run_test.py
import pytest
if __name__ == '__main__':
pytest.main(['-v'])
'''
============================= test session starts =============================
platform win32 -- Python 3.7.0, pytest-5.3.4, py-1.8.1, pluggy-0.13.1 -- D:\Python3.
cachedir: .pytest_cache
rootdir: D:\Python3.7\project\pytest, inifile: pytest.ini
plugins: allure-pytest-2.8.9, rerunfailures-8.0
collecting ... collected 2 items
test_case/test_func.py::TestFunc::test_add_by_class PASSED              [ 50%]
test_case/test_func.py::test_add_by_func_aaa FAILED                      [100%]
================================== FAILURES ===================================
____________________________ test_add_by_func_aaa _____________________________
def test_add_by_func_aaa():
with pytest.raises(TypeError) as E:
add('3',4)
pe)
print(E.value)
aceback)
>    assert 1 == 2
E    assert 1 == 2
E      -1
E      +2
test_case\test_func.py:18: AssertionError
-
--------------------------- Captured stdout call -----------------------------
<class 'TypeError'>
数据类型错误
[<TracebackEntry D:\Python3.7\project\pytest\test_case\test_func.py:14>, <TracebackEntry D:\Python3.7\project\pytest\func.py:6>]
========================= 1 failed, 1 passed in 0.10s =========================
[Finished in 1.4s]
'''
控制台输出的“Captured stdout call”就是异常的信息,包含类型,异常说明,异常跟踪信息。
可以通过assert断⾔这些信息。
也可以通过给pytest.raises()传⼊match关键字参数来完成E.value的断⾔,这⾥运⽤到的是python中正则表达式的原理。
⽰例:
该⽰例意味断⾔通过
def test_add_by_func_aaa():
with pytest.raises(TypeError, match=r'.*类型错误$') as E:
add('3',4)
该⽰例意味断⾔失败:
# 异常测试⽤例,期望结果为爆出TypeError异常
def test_add_by_func_aaa():
with pytest.raises(TypeError, match=r'.*正确$') as E:
add('3',4)
'''
During handling of the above exception, another exception occurred:
def test_add_by_func_aaa():
with pytest.raises(TypeError, match=r'.*正确$') as E:
>    add('3',4)
E    AssertionError: Pattern '.*正确$' not found in '数据类型错误'
test_case\test_func.py:14: AssertionError
'''
如果,某个测试⽤例可能出现不同的预期异常,只要爆出的异常在预期的⼏个异常之内,那么如何断⾔呢。解决⽅法很简单,原理和接⼝都没变,只是在pytest.raises()中传⼊异常类型的参数,从传⼊⼀个异常类型,改变为传⼊⼀个异常类型组成的元组。同样只是传⼊⼀个参数。
⽰例:
# ./func.py
def add(a,b):
raise NameError('名字错了')
if isinstance(a,int) and isinstance(b,int):
return a+b
else:
raise TypeError('数据类型错误')
# ./test_case/test_func.py
import pytest
from func import *
'''
class TestFunc:
# 正常测试⽤例
def test_add_by_class(self):
assert add(2,3) == 5
'''