上一章学习了pytest的基本用法,今天学习一下断言。

1、assert 基本用法

unitest单元测试框架中提供了丰富的断言方法,如assertEqual()、assertIn()、assertTrue()、assertIs()等,但是pytest没有。直接使用Python的assert进行断言

  1. # MyPytest.py
  2. import pytest
  3. def inc(x):
  4. return x+1
  5. # 判断结果不等于5
  6. def test_inc_01():
  7. assert inc(3) != 5
  8. # 判断结果等于4
  9. def test_inc_02():
  10. assert inc(3) == 4
  11. # 判断结果小于等于5
  12. def test_inc_03():
  13. assert inc(3) <= 5
  14. # 判断结果包含在列表内
  15. def test_inc_04():
  16. assert inc(3) in [1,2,3,4]
  17. # 判断结果不为True
  18. def test_inc_06():
  19. a = 1
  20. assert a == True
  21. assert a is not True
  22. if __name__ =="__main__":
  23. pytest.main(['MyPytest.py'])

执行结果:

  1. collected 5 items
  2. MyPytest.py ..... [100%]
  3. ============================== 5 passed in 0.08s ==============================
  4. ***Repl Closed***

2、assert断言失败提示

当我们为了脚本报错后更容易的定位到原因时候,可以在断言的地方输出断言失败提示信息,比如:

  1. # MyPytest.py
  2. import pytest
  3. def division(x):
  4. return 100/x
  5. def test_division_01():
  6. res = division(3)
  7. assert res == 50 , f"判断 res 为50 ,当前 res 的值为{res}"
  8. if __name__ =="__main__":
  9. pytest.main(['MyPytest.py'])

执行结果:

  1. collected 1 item
  2. MyPytest.py F [100%]
  3. ================================== FAILURES ===================================
  4. ______________________________ test_division_01 _______________________________
  5. def test_division_01():
  6. res = division(3)
  7. > assert res == 50 , f"判断 res 为50 ,当前 res 的值为{res}"
  8. E AssertionError: 判断 res 50 ,当前 res 的值为33.333333333333336
  9. E assert 33.333333333333336 == 50
  10. MyPytest.py:10: AssertionError
  11. =========================== short test summary info ===========================
  12. FAILED MyPytest.py::test_division_01 - AssertionError: 判断 res 50 ,当前 r...
  13. ============================== 1 failed in 0.21s ==============================
  14. ***Repl Closed***

3、预期内异常报错断言

有时候一些场景我们明知道它会报错,也知道这种报错,是正常的预期,比如:

  1. # MyPytest.py
  2. import pytest
  3. def division(x):
  4. return 100/x
  5. def test_division_01():
  6. res = division(0)
  7. if __name__ =="__main__":
  8. pytest.main(['MyPytest.py'])

执行结果:

  1. collected 1 item
  2. MyPytest.py F [100%]
  3. ================================== FAILURES ===================================
  4. ______________________________ test_division_01 _______________________________
  5. def test_division_01():
  6. > res = division(0)
  7. MyPytest.py:9:
  8. _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
  9. x = 0
  10. def division(x):
  11. > return 100/x
  12. E ZeroDivisionError: division by zero
  13. MyPytest.py:5: ZeroDivisionError
  14. =========================== short test summary info ===========================
  15. FAILED MyPytest.py::test_division_01 - ZeroDivisionError: division by zero
  16. ============================== 1 failed in 0.21s ==============================
  17. ***Repl Closed***

100 \ 0的时候报错ZeroDivisionError: division by zero。为了对这种异常场景进行断言,我们需要使用pytest.raises,用法如下:

  1. # MyPytest.py
  2. import pytest
  3. def division(x):
  4. return 100/x
  5. def test_division_01():
  6. with pytest.raises(ZeroDivisionError) as e:
  7. division(0)
  8. assert e.type == ZeroDivisionError
  9. assert "division by zero" in str(e.value)
  10. if __name__ =="__main__":
  11. pytest.main(['MyPytest.py'])

注意:断言 type 的时候,异常类型是不需要加引号的,断言 value 值的时候需转 str

4、非预期内异常

如果我们不知道预期异常的是什么,我们可以使用matchraise进行自定义异常

  1. # MyPytest.py
  2. import pytest
  3. def division(x):
  4. if x == 0:
  5. raise ValueError('value not 0 or None')
  6. if isinstance(x,str):
  7. raise TypeError('value not String')
  8. return 100/x
  9. def test_division_01():
  10. with pytest.raises(ValueError,match = 'value not 0 or None') as e:
  11. division(0)
  12. assert e.type == ValueError
  13. assert "value not 0 or None" in str(e.value)
  14. def test_division_02():
  15. with pytest.raises(TypeError,match = 'value not String') as e:
  16. division("String")
  17. assert e.type == TypeError
  18. assert "value not String" in str(e.value)
  19. if __name__ =="__main__":
  20. pytest.main(['MyPytest.py'])

5、pytest-assume插件

pytest-assume是一个可以允许pytest测试用例中执行多个失败的断言的插件。
安装

pip install pytest-assume

对比assert :

  1. import pytest
  2. def inc(x):
  3. return x+1
  4. # assert断言
  5. def test_inc_01():
  6. assert inc(3) == 5
  7. assert inc(3) == 4
  8. assert inc(3) == 3
  9. # pytest.assume断言
  10. def test_inc_02():
  11. pytest.assume(inc(3) == 5)
  12. pytest.assume(inc(3) == 4)
  13. pytest.assume(inc(3) == 3)
  14. if __name__ =="__main__":
  15. pytest.main(['MyPytest.py'])

结果:

  1. collected 2 items
  2. MyPytest.py FF [100%]
  3. ================================== FAILURES ===================================
  4. _________________________________ test_inc_01 _________________________________
  5. def test_inc_01():
  6. > assert inc(3) == 5
  7. E assert 4 == 5
  8. E + where 4 = inc(3)
  9. MyPytest.py:8: AssertionError
  10. _________________________________ test_inc_02 _________________________________
  11. tp = <class 'pytest_assume.plugin.FailedAssumption'>, value = None, tb = None
  12. def reraise(tp, value, tb=None):
  13. try:
  14. if value is None:
  15. value = tp()
  16. if value.__traceback__ is not tb:
  17. > raise value.with_traceback(tb)
  18. E pytest_assume.plugin.FailedAssumption:
  19. E 2 Failed Assumptions:
  20. E
  21. E MyPytest.py:15: AssumptionFailure
  22. E >> pytest.assume(inc(3) == 5)
  23. E AssertionError: assert False
  24. E
  25. E MyPytest.py:17: AssumptionFailure
  26. E >> pytest.assume(inc(3) == 3)
  27. E AssertionError: assert False
  28. D:\software\python\lib\site-packages\six.py:718: FailedAssumption
  29. =========================== short test summary info ===========================
  30. FAILED MyPytest.py::test_inc_01 - assert 4 == 5
  31. FAILED MyPytest.py::test_inc_02 - pytest_assume.plugin.FailedAssumption:
  32. ============================== 2 failed in 0.26s ==============================
  33. ***Repl Closed***

对比发现,pytest.assume在第一个断言失败的情况下继续执行后续的断言,不会终止~