简介
Pytest是基于python的一种单元测试框架,与python自带的unittest测试框架类似,但是比unittest框架使用起来更简洁,效率更高。
The pytest framework makes it easy to write small tests, yet scales to support complex functional testing for applications and libraries.
pytest框架使编写小型测试变得很容易,同时还支持对应用程序和库进行复杂的功能测试。
pytest官方主页
优点
- 允许直接使用
assert
进行断言,而不需要使用self.assert*
;
- 可以自动寻找单测文件、类和函数;
Modular fixtures
可以用于管理小型或参数化的测试信息;
- 与
unittest
和nose
单测框架兼容;
- 兼容性较好,支持Python 2.7,Python 3.4+。
- 丰富的插件支持,共计有超过315个插件支持;
安装
1.安装命令
2.安装校验
1 2
| pytest --version This is pytest version 3.x.y, imported from $PYTHON_PREFIX/lib/python3.6/site-packages/pytest.py
|
入门使用
创建一个测试模块,test_add.py定义如下
1 2 3 4 5 6 7
| def add(a,b): return a+b
def test_add(): assert add(2,3)==4
|
打开cmd执行如下命令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| λ pytest E:\code\learning_python\pytest_test\test_add.py ============================= test session starts ============================= platform win32 -- Python 3.5.0, pytest-3.8.1, py-1.5.3, pluggy-0.7.1 rootdir: E:\, inifile: collected 1 item
..\..\..\code\learning_python\pytest_test\test_add.py F [100%]
================================== FAILURES =================================== __________________________________ test_add ___________________________________
def test_add(): > assert add(2,3)==4 E assert 5 == 4 E + where 5 = add(2, 3)
E:\code\learning_python\pytest_test\test_add.py:6: AssertionError ========================== 1 failed in 0.27 seconds ===========================
|
在pytest框架中,有如下约束:
- 所有的单测文件名都需要满足
test_*.py
格式或*_test.py
格式。
- 在单测文件中,可以包含
test_
开头的函数,也可以包含Test
开头的类。
- 在单测类中,可以包含一个或多个
test_
开头的函数。
执行多个测试用例
test_class.py
1 2 3 4 5 6 7 8 9 10 11
| class TestClass(): def test_one(self): print('test one') x='hello' assert 'h' in x
def test_two(self): print('test two') y='mgtv' assert 'm' in y
|
执行命令:
1 2 3 4 5
| λ pytest -q -s E:\code\learning_python\pytest_test\test_class.py test one .test two . 2 passed in 0.02 seconds
|
参数说明
-q
安静模式运行用例,显示简要信息
-s
显示(show)用例打印的信息
如果不加这两个参数显示效果如下:
1 2 3 4 5 6 7 8 9
| λ pytest E:\code\learning_python\pytest_test\test_class.py ============================= test session starts ============================= platform win32 -- Python 3.5.0, pytest-3.8.1, py-1.5.3, pluggy-0.7.1 rootdir: E:\, inifile: collected 2 items
..\..\..\code\learning_python\pytest_test\test_class.py .. [100%]
========================== 2 passed in 0.04 seconds ===========================
|
setup与teardown
PyTest
支持xUnit style
结构, setup()
和 teardown()
方法用于初始化和清理测试环境,可以保证测试用例的独立性。pytest
的setup/teardown
方法包括:
- 模块级别(
setup_module/teardown_module
)
- 函数级别(
setup_function/teardown_function
)
- 类级别(
setup_class/ teardown_class
)
- 方法级别(
setup_method/teardown_methond
或者setup/teardown
)
模块级别
模块中的第一个测试用例开始前执行setup_module
方法,模块中的最后一个测试用例结束后运行teardown_module
方法,不在测试类中。
函数级别
setup_function/teardown_function
在每个测试函数前后运行,只对函数用例生效,不在类中。
类级别
类级别函数 setup_class/teardown_class
对类有效,位于类中,在测试类中前后调用一次。
方法级别
方法级别函数 setup_method/teardown_method
和setup/teardown
对类有效,也位于类中,这两个效果一样,在测试类中每个测试方法前后调用一次。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import pytest
class TestPytest():
def setup_class(self): print('setup_class:每个测试类开始之前都会执行')
def teardown_class(self): print('teardown_class:每个测试类结束之后都会执行')
def setup_method(self): print("setup_method:每个用例开始前都会执行")
def teardown_method(self): print("teardown_method:每个用例结束后都会执行")
def test_one(self): print("正在执行---test_one")
def test_two(self): print("正在执行---test_two")
def login(self): print("正在执行---test_login")
if __name__ == "__main__": pytest.main(["-s","pytest_setup_teardown.py"])
|
运行结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| ============================= test session starts ============================= platform win32 -- Python 3.7.3, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 rootdir: C:\Users\Shuqing\PycharmProjects\python_testframwork collected 2 items
pytest_setup_teardown.py setup_class:每个测试类开始之前都会执行 setup_function:每个用例开始前都会执行 正在执行---test_one .teardown_function:每个用例结束后都会执行 setup_function:每个用例开始前都会执行 正在执行---test_two .teardown_function:每个用例结束后都会执行 teardown_class:每个测试类结束之后都会执行
============================== 2 passed in 0.03s ==============================
Process finished with exit code 0
|
参数化
@pytest.mark.parametrize
可以为测试方法或者测试类定义多组变量。
参数化方法如下:
1 2 3 4 5 6 7 8
| @pytest.mark.parametrize(argnames, argvalues)
argnames:参数化变量名,可以是string(逗号分割) 、list 和 tuple类型
@pytest.mark.parametrize(“a, b”, [(1,2),(3,4)]) @pytest.mark.parametrize([“a”,“b”], [(1,2),(3, 4)]) @pytest.mark.parametrize((“a”, “b”), [(1,2),(3,4)]) argvalues:参数化的值
|
参数化案例1
1 2 3 4 5 6 7 8 9 10 11 12
| import pytest
class Test_Demo(): @pytest.mark.parametrize("a, b, result", [(1, 1, 2), (2, 8, 10)]) def test_case1(self, a, b, result): print("\n开始执行测试用例") print(a, b, result) assert a + b == result
if __name__ == '__main__': pytest.main(["-s","pytest_param.py"])
|
运行结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| C:\python37\python.exe C:/Users/Shuqing/PycharmProjects/python_testframwork/pytest_param.py ============================= test session starts ============================= platform win32 -- Python 3.7.3, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 rootdir: C:\Users\Shuqing\PycharmProjects\python_testframwork collected 2 items
pytest_param.py 开始执行测试用例 1 1 2 . 开始执行测试用例 2 8 10 .
============================== 2 passed in 0.02s ==============================
Process finished with exit code 0
|
参数化案例2
数据驱动参数化的应用,数据量小的测试用例可以使用代码的参数化来实现数据驱动,数据量大的情况下可以使用一种结构化的文件(例如csv、yaml、xml、db、 excel、json
等)来存储数据,然后在测试用例中读取这些数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import pytest,yaml
class Test_Demo(): @pytest.mark.parametrize("a, b, result", yaml.safe_load(open("./data.yaml"))) def test_case1(self, a, b, result): print("\n开始执行测试用例") print(a, b, result) assert a + b == result
if __name__ == '__main__': pytest.main(["-s","pytest_param.py"])
|
参考资料