Pytest教程展示了如何使用pytest模块测试Python应用程序。
Python测试
Pytest是一个用于测试Python应用程序的Python库。它是nose和unittest的替代品。
pytest安装
Pytest使用以下命令安装:
$ pip install pytest
这将安装pytest
库。
pytest测试发现约定
如果未指定参数,则在testpaths
(如果已配置)或当前目录中的位置搜索测试文件。或者,命令行参数可以用于目录、文件名或节点ID的任意组合。
在选定的目录中,pytest查找test_*.py
或*_test.py
文件。在选定的文件中,pytest在类外部查找带test前缀的测试函数,并在带Test前缀的测试类中查找带test前缀的测试方法(没有__init__
方法)。
运行测试
在没有参数的情况下,pytest查看当前工作目录(或其他一些预先配置的目录)和测试文件的所有子目录,并运行它找到的测试代码。
$ pytest
运行当前目录下的所有测试文件。
$ pytest min_max_test.py
我们可以通过将其名称作为参数来运行特定的测试文件。
$ pytest min_max_test.py::test_min
可以通过在::
字符后提供其名称来运行特定函数。
$ pytest -m smoke
标记可用于对测试进行分组。然后使用pytest-m
运行一组标记的测试。
$ pytest -k <expression>
此外,我们可以使用表达式来运行与测试函数和类名称匹配的测试。
Pythonpytest简单例子
在第一个示例中,我们将使用pytest测试两个简单的数学算法。
def max(values): _max = values[0] for val in values: if val > _max: _max = val return _max def min(values): _min = values[0] for val in values: if val < _min: _min = val return _min
我们有一个带有自定义max
和min
函数的模块。
#!/usr/bin/python import algo def test_min(): values = (2, 3, 1, 4, 6) val = algo.min(values) assert val == 1 def test_max(): values = (2, 3, 1, 4, 6) val = algo.max(values) assert val == 6
测试文件min_max_test.py
的名称中有一个测试词。
def test_min(): values = (2, 3, 1, 4, 6) val = algo.min(values) assert val == 1
另外,测试函数test_min
有一个测试词。我们使用assert
关键字来测试算法的值。
$ pytest min_max_test.py ================================================= test session starts ================================================= platform win32 -- Python 3.7.0, pytest-5.0.1, py-1.8.0, pluggy-0.12.0 rootdir: C:\Users\Jano\Documents\pyprogs\pytest collected 2 items min_max_test.py .. [100%] ============================================== 2 passed in 0.03 seconds ===============================================
这是输出。有两个测试都已成功通过。更详细的输出显示为pytest-vmin_max_test.py
。
Pytest跳过
使用skip装饰器,我们可以跳过指定的测试。跳过测试的原因有多种;例如,数据库/在线服务目前不可用,或者我们跳过Windows上的Linux特定测试。
#!/usr/bin/python import algo import pytest @pytest.mark.skip def test_min(): values = (2, 3, 1, 4, 6) val = algo.min(values) assert val == 1 def test_max(): values = (2, 3, 1, 4, 6) val = algo.max(values) assert val == 6
在示例中,test_min
被跳过。
$ pytest min_max_test.py ================================================= test session starts ================================================= platform win32 -- Python 3.7.0, pytest-5.0.1, py-1.8.0, pluggy-0.12.0 rootdir: C:\Users\Jano\Documents\pyprogs\pytest collected 2 items min_max_test.py s. [100%] ========================================= 1 passed, 1 skipped in 0.04 seconds =========================================
在测试文件名后面的输出中,s代表skipped和.为通过。
Pytest标记
我们可以使用标记将测试组织成单元。
#!/usr/bin/python # pytest -m a marking.py # pytest -m b marking.py import pytest @pytest.mark.a def test_a1(): assert (1) == (1) @pytest.mark.a def test_a2(): assert (1, 2) == (1, 2) @pytest.mark.a def test_a3(): assert (1, 2, 3) == (1, 2, 3) @pytest.mark.b def test_b1(): assert "falcon" == "fal" + "con" @pytest.mark.b def test_b2(): assert "falcon" == f"fal{'con'}"
我们有两组由标记a和b标识的测试。这些单元由pytest-mamarking.py
和pytest-mbmarking.py
运行。
Pytest参数化测试
通过参数化测试,我们可以向断言中添加多个值。我们使用@pytest.mark.parametrize
标记。
#!/usr/bin/python import algo import pytest @pytest.mark.parametrize("data, expected", [((2, 3, 1, 4, 6), 1), ((5, -2, 0, 9, 12), -2), ((200, 100, 0, 300, 400), 0)]) def test_min(data, expected): val = algo.min(data) assert val == expected @pytest.mark.parametrize("data, expected", [((2, 3, 1, 4, 6), 6), ((5, -2, 0, 9, 12), 12), ((200, 100, 0, 300, 400), 400)]) def test_max(data, expected): val = algo.max(data) assert val == expected
在示例中,我们使用多个输入数据测试这两个函数。
@pytest.mark.parametrize("data, expected", [((2, 3, 1, 4, 6), 1), ((5, -2, 0, 9, 12), -2), ((200, 100, 0, 300, 400), 0)]) def test_min(data, expected): val = algo.min(data) assert val == expected
我们将两个值传递给测试函数:数据和预期值。在我们的例子中,我们使用三个数据元组测试min
函数。
$ pytest parameterized.py ================================================= test session starts ================================================= platform win32 -- Python 3.7.0, pytest-5.0.1, py-1.8.0, pluggy-0.12.0 rootdir: C:\Users\Jano\Documents\pyprogs\pytest collected 6 items parametrized.py ...... [100%] ============================================== 6 passed in 0.03 seconds ===============================================
Pytest输出表明有六次运行。
Pytest固定装置
测试需要在一组已知对象的背景下运行。这组对象称为测试夹具。
def sel_sort(data): if not isinstance(data, list): vals = list(data) else: vals = data size = len(vals) for i in range(0, size): for j in range(i+1, size): if vals[j] < vals[i]: _min = vals[j] vals[j] = vals[i] vals[i] = _min return vals ...
对于此示例,我们将选择排序算法添加到algo.py
模块。
#!/usr/bin/python import algo import pytest @pytest.fixture def data(): return [3, 2, 1, 5, -3, 2, 0, -2, 11, 9] def test_sel_sort(data): sorted_vals = algo.sel_sort(data) assert sorted_vals == sorted(data)
我们用夹具测试选择排序。
@pytest.fixture def data(): return [3, 2, 1, 5, -3, 2, 0, -2, 11, 9]
我们的测试夹具只是返回一些测试数据。请注意,我们通过名称引用此夹具:data
。
def test_sel_sort(data): sorted_vals = algo.sel_sort(data) assert sorted_vals == sorted(data)
在test_sel_sort
函数中,我们将数据夹具作为函数参数传递。
$ pytest fixtures.py ================================================= test session starts ================================================= platform win32 -- Python 3.7.0, pytest-5.0.1, py-1.8.0, pluggy-0.12.0 rootdir: C:\Users\Jano\Documents\pyprogs\pytest collected 1 item fixtures.py . [100%] ============================================== 1 passed in 0.02 seconds ===============================================
Pytest布局
Python测试可以通过多种方式组织。测试可以集成在Python包中,也可以放在包外。
集成测试
接下来我们将展示如何在Python包中运行测试。
setup.py utils â algo.py â srel.py â __init__.py â ââââtests algo_test.py srel_test.py __init__.py
我们有这个包布局。测试位于包内的tests
子目录中。
#!/usr/bin/python from setuptools import setup, find_packages setup(name="utils", packages=find_packages())
这是setup.py
。
def sel_sort(data): if not isinstance(data, list): vals = list(data) else: vals = data size = len(vals) for i in range(0, size): for j in range(i+1, size): if vals[j] < vals[i]: _min = vals[j] vals[j] = vals[i] vals[i] = _min return vals def max(values): _max = values[0] for val in values: if val > _max: _max = val return _max def min(values): _min = values[0] for val in values: if val < _min: _min = val return _min
这是algo.py
文件。
def is_palindrome(val): return val == val[::-1]
我们还有另一个模块,其中包含一个函数来测试一个单词是否是回文。
#!/usr/bin/python import utils.algo import pytest @pytest.fixture def data(): return [3, 2, 1, 5, -3, 2, 0, -2, 11, 9] def test_sel_sort(data): sorted_vals = utils.algo.sel_sort(data) assert sorted_vals == sorted(data) def test_min(): values = (2, 3, 1, 4, 6) val = utils.algo.min(values) assert val == 1 def test_max(): values = (2, 3, 1, 4, 6) val = utils.algo.max(values) assert val == 6
这些是utils.algo
模块的测试。请注意,我们使用完整的模块名称。
#!/usr/bin/python import utils.srel import pytest @pytest.mark.parametrize("word, expected", [('kayak', True), ('civic', True), ('forest', False)]) def test_palindrome(word, expected): val = utils.srel.is_palindrome(word) assert val == expected
这是对is_palindrome
函数的测试。
两个__init__.py
文件都是空的。
$ pytest --pyargs utils ================================================= test session starts ================================================= platform win32 -- Python 3.7.0, pytest-5.0.1, py-1.8.0, pluggy-0.12.0 rootdir: C:\Users\Jano\Documents\pyprogs\pytest\structure collected 6 items utils\tests\algo_test.py ... [ 50%] utils\tests\srel_test.py ... [100%] ============================================== 6 passed in 0.06 seconds ===============================================
我们使用pytest--pyargsutils
命令运行测试。
包外测试
下一个示例显示了一个应用程序源代码布局,其中测试未集成到包中。
setup.py src ââââutils â algo.py â srel.py tests algo_test.py srel_test.py
在此布局中,我们在源代码树之外进行了测试。请注意,不需要__init__.py
文件。
$ set PYTHONPATH=src $ pytest
我们设置PYTHONPATH
并运行pytest。
在本教程中,我们介绍了Pythonpytest库。
阅读Python教程或列出所有Python教程。