什么是断言呢?简单来说就是实际结果和期望结果去对比。
一 断言用法
在pytest中,使用assert进行断言,格式为:assert 表达式。
如果表达式返回结果为True,则断言成功,否则断言失败。
二 常用断言
unittest
的三种断言:
assertIn(expect,result)断言包含(被包含的写前面);
assertEqual(expect,result)断言相等;
assertTure(条件)断言是否为真。返回Ture或False;
Pytest
里的断言实际上就是Python中的assert断言方法,常用断言方法如下:
- assert xx :判断 xx 为真;
- assert not xx :判断 xx 不为真;
- assert a in b :判断 b 包含 a;
- assert a == b :判断 a 等于 b;
- assert a != b :判断 a 不等于 b;
import pytest def test_demo1(): a = 1 assert a def test_demo2(): a = 0 assert not a def test_demo3(): s = 'hello' assert 'h' in s def test_demo4(): a = 3 assert a == 3 def test_demo5(): a = 4 assert a != 3 if __name__ == '__main__': pytest.main()
运行结果如下:
Testing started at 18:22 ... C:\Users\96984\Desktop\code\pytest\venv\Scripts\python.exe "C:\ruanjian\pycharm2019.3\PyCharm 2019.3.1\plugins\python\helpers\pycharm\_jb_pytest_runner.py" --path C:/Users/96984/Desktop/code/pytest/demo/demo_pytest.py Launching pytest with arguments C:/Users/96984/Desktop/code/learn_pytest/demo/demo_pytest.py in C:\Users\96984\Desktop\code\learn_pytest\demo ============================= test session starts ============================= platform win32 -- Python 3.6.8, pytest-5.4.3, py-1.9.0, pluggy-0.13.1 -- C:\Users\96984\Desktop\code\learn_pytest\venv\Scripts\python.exe cachedir: .pytest_cache metadata: {'Python': '3.6.8', 'Platform': 'Windows-10-10.0.18362-SP0', 'Packages': {'pytest': '5.4.3', 'py': '1.9.0', 'pluggy': '0.13.1'}, 'Plugins': {'html': '2.1.1', 'metadata': '1.10.0'}, 'JAVA_HOME': 'C:\\Program Files\\Java\\jdk1.8.0_77'} rootdir: C:\Users\96984\Desktop\code\learn_pytest\demo plugins: html-2.1.1, metadata-1.10.0 collecting ... collected 5 items demo_pytest.py::test_demo1 PASSED [ 20%] demo_pytest.py::test_demo2 PASSED [ 40%] demo_pytest.py::test_demo3 PASSED [ 60%] demo_pytest.py::test_demo4 PASSED [ 80%] demo_pytest.py::test_demo5 PASSED [100%] ============================== 5 passed in 0.06s ============================== Process finished with exit code 0
三 异常断言
在测试过程中,有时需要对特定异常进行断言,可以使用 pytest.raises 作为上下文管理器,当抛出异常时可以获取到对应的异常实例。
import pytest def test_zero_division(): 1 / 0 if __name__ == '__main__': pytest.main()
运行结果:
================================== FAILURES =================================== _____________________________ test_zero_division ______________________________ def test_zero_division(): > 1 / 0 E ZeroDivisionError: division by zero
所以我们需要捕获并断言异常。
断言场景:断言抛出的异常是否符合预期。
预期结果:ZeroDivisionError: division by zero,其中ZeroDivisionError为错误类型,division by zero为具体错误值。
断言方式: 断言异常的type和value值。
断言代码如下:
import pytest def test_zero_division_long(): with pytest.raises(ZeroDivisionError) as excinfo: 1 / 0 # 断言异常类型 type assert excinfo.type == ZeroDivisionError # 断言异常 value 值 assert "division by zero" in str(excinfo.value) if __name__ == '__main__': pytest.main()
① pytest.raises 捕获异常,源码如下:
def raises( # noqa: F811 expected_exception: Union["Type[_E]", Tuple["Type[_E]", ...]], *args: Any, **kwargs: Any ) -> Union["RaisesContext[_E]", _pytest._code.ExceptionInfo[_E]]: __tracebackhide__ = True for exc in filterfalse( inspect.isclass, always_iterable(expected_exception, BASE_TYPE) # type: ignore[arg-type] # noqa: F821 ): msg = "exceptions must be derived from BaseException, not %s" raise TypeError(msg % type(exc)) message = "DID NOT RAISE {}".format(expected_exception) if not args: match = kwargs.pop("match", None) if kwargs: msg = "Unexpected keyword arguments passed to pytest.raises: " msg += ", ".join(sorted(kwargs)) msg += "\nUse context-manager form instead?" raise TypeError(msg) return RaisesContext(expected_exception, message, match) else: func = args[0] if not callable(func): raise TypeError( "{!r} object (type: {}) must be callable".format(func, type(func)) ) try: func(*args[1:], **kwargs) except expected_exception as e: # We just caught the exception - there is a traceback. assert e.__traceback__ is not None return _pytest._code.ExceptionInfo.from_exc_info( (type(e), e, e.__traceback__) ) fail(message) raises.Exception = fail.Exception # type: ignore
② excinfo作为异常信息实例,拥有type 、value等属性,源码如下:
@property def type(self) -> "Type[_E]": """the exception class""" assert ( self._excinfo is not None ), ".type can only be used after the context manager exits" return self._excinfo[0] @property def value(self) -> _E: """the exception value""" assert ( self._excinfo is not None ), ".value can only be used after the context manager exits" return self._excinfo[1]
③ excinfo.value的值是元组,所以要转成字符串。
自定义异常与pytest断言结合
errors.py(格式唯一,一定要这么写)
class Exc(Exception):
def __init__(self,ErrorInfo):
super().__init__(self) #初始化父类
self.errorinfo=ErrorInfo
def __str__(self):
return self.errorinfo
testlogin2.py
import pytest
from pytestT.errors import Exc
from pytestT.errors import Exc2
class Test_login(object):
def f(self,a):
if a==1:
raise Exc('异常')
else:
raise Exc2
def test_T(self):
with pytest.raises(Exc) as F:
self.f(1)
print('***************')
# assert F.type==Exc
assert str(F.value)=='异常'
print('**************')
print(F.value)