原文: http://zetcode.com/python/pytest/

Pytest 教程展示了如何使用 Pytest 模块测试 Python 应用。

python Pytest

Pytest 是用于测试 Python 应用的 Python 库。 它是鼻子测试和单元测试的替代方法。

Pytest 安装

使用以下命令安装 Pytest:

  1. $ pip install pytest

这将安装 Pytest 库。

Pytest 测试发现约定

如果未指定任何参数,则在testpaths(如果已配置)或当前目录中的位置搜索测试文件。 另外,命令行参数可以在目录,文件名或节点 ID 的任何组合中使用。

Pytest 在所选目录中查找test_*.py*_test.py文件。 在选定的文件中,Pytest 在类之外查找带前缀的测试函数,并在带前缀的测试类中查找带前缀的测试方法(无__init__()方法)。

运行 Pytest

Pytest 不带任何参数,将查看当前工作目录(或其他一些预配置的目录)以及测试文件的所有子目录,并运行找到的测试代码。

  1. $ pytest

运行当前目录中的所有测试文件。

  1. $ pytest min_max_test.py

我们可以通过指定名称作为参数来运行特定的测试文件。

  1. $ pytest min_max_test.py::test_min

可以通过在::字符后提供其名称来运行特定函数。

  1. $ pytest -m smoke

标记可用于对测试进行分组。 然后使用pytest -m运行一组标记的测试。

  1. $ pytest -k <expression>

另外,我们可以使用表达式来运行与测试函数和类的名称匹配的测试。

Python Pytest 简单示例

在第一个示例中,我们将使用 Pytest 测试两个简单的数学算法。

algo.py

  1. def max(values):
  2. _max = values[0]
  3. for val in values:
  4. if val > _max:
  5. _max = val
  6. return _max
  7. def min(values):
  8. _min = values[0]
  9. for val in values:
  10. if val < _min:
  11. _min = val
  12. return _min

我们有一个带有自定义max()min()函数的模块。

min_max_test.py

  1. #!/usr/bin/env python3
  2. import algo
  3. def test_min():
  4. values = (2, 3, 1, 4, 6)
  5. val = algo.min(values)
  6. assert val == 1
  7. def test_max():
  8. values = (2, 3, 1, 4, 6)
  9. val = algo.max(values)
  10. assert val == 6

测试文件min_max_test.py的名称中包含一个测试词。

  1. def test_min():
  2. values = (2, 3, 1, 4, 6)
  3. val = algo.min(values)
  4. assert val == 1

此外,测试函数test_min()具有测试字。 我们使用assert关键字来测试算法的值。

  1. $ pytest min_max_test.py
  2. ================================================= test session starts =================================================
  3. platform win32 -- Python 3.7.0, pytest-5.0.1, py-1.8.0, pluggy-0.12.0
  4. rootdir: C:\Users\Jano\Documents\pyprogs\pytest
  5. collected 2 items
  6. min_max_test.py .. [100%]
  7. ============================================== 2 passed in 0.03 seconds ===============================================

这是输出。 有两个测试,并且都成功通过了。 pytest -v min_max_test.py显示了更详细的输出。

Pytest 跳过

使用跳过装饰器,我们可以跳过指定的测试。 跳过测试有多种原因。 例如,数据库/在线服务目前不可用,或者我们跳过了 Windows 上针对 Linux 的特定测试。

skipping.py

  1. #!/usr/bin/env python3
  2. import algo
  3. import pytest
  4. @pytest.mark.skip
  5. def test_min():
  6. values = (2, 3, 1, 4, 6)
  7. val = algo.min(values)
  8. assert val == 1
  9. def test_max():
  10. values = (2, 3, 1, 4, 6)
  11. val = algo.max(values)
  12. assert val == 6

在示例中,test_min()被跳过。

  1. $ pytest min_max_test.py
  2. ================================================= test session starts =================================================
  3. platform win32 -- Python 3.7.0, pytest-5.0.1, py-1.8.0, pluggy-0.12.0
  4. rootdir: C:\Users\Jano\Documents\pyprogs\pytest
  5. collected 2 items
  6. min_max_test.py s. [100%]
  7. ========================================= 1 passed, 1 skipped in 0.04 seconds =========================================

在测试文件名后面的输出中,s代表跳过的和。 通过。

Pytest 标记

我们可以使用标记将测试组织为单元。

marking.py

  1. #!/usr/bin/env python3
  2. # pytest -m a marking.py
  3. # pytest -m b marking.py
  4. import pytest
  5. @pytest.mark.a
  6. def test_a1():
  7. assert (1) == (1)
  8. @pytest.mark.a
  9. def test_a2():
  10. assert (1, 2) == (1, 2)
  11. @pytest.mark.a
  12. def test_a3():
  13. assert (1, 2, 3) == (1, 2, 3)
  14. @pytest.mark.b
  15. def test_b1():
  16. assert "falcon" == "fal" + "con"
  17. @pytest.mark.b
  18. def test_b2():
  19. assert "falcon" == f"fal{'con'}"

我们有两组由标记ab标识的测试。 这些单元由pytest -m a marking.pypytest -m b marking.py运行。

Pytest 参数化测试

通过参数化测试,我们可以向断言中添加多个值。 我们使用@pytest.mark.parametrize标记。

parametrized.py

  1. #!/usr/bin/env python3
  2. import algo
  3. import pytest
  4. @pytest.mark.parametrize("data, expected", [((2, 3, 1, 4, 6), 1),
  5. ((5, -2, 0, 9, 12), -2), ((200, 100, 0, 300, 400), 0)])
  6. def test_min(data, expected):
  7. val = algo.min(data)
  8. assert val == expected
  9. @pytest.mark.parametrize("data, expected", [((2, 3, 1, 4, 6), 6),
  10. ((5, -2, 0, 9, 12), 12), ((200, 100, 0, 300, 400), 400)])
  11. def test_max(data, expected):
  12. val = algo.max(data)
  13. assert val == expected

在示例中,我们使用多个输入数据测试这两个函数。

  1. @pytest.mark.parametrize("data, expected", [((2, 3, 1, 4, 6), 1),
  2. ((5, -2, 0, 9, 12), -2), ((200, 100, 0, 300, 400), 0)])
  3. def test_min(data, expected):
  4. val = algo.min(data)
  5. assert val == expected

我们将两个值传递给测试函数:数据和期望值。 在我们的例子中,我们用三个数据元组测试min()函数。

  1. $ pytest parametrized.py
  2. ================================================= test session starts =================================================
  3. platform win32 -- Python 3.7.0, pytest-5.0.1, py-1.8.0, pluggy-0.12.0
  4. rootdir: C:\Users\Jano\Documents\pyprogs\pytest
  5. collected 6 items
  6. parametrized.py ...... [100%]
  7. ============================================== 6 passed in 0.03 seconds ===============================================

Pytest 输出告知有六次运行。

Pytest 夹具

测试需要在一组已知对象的背景下进行。 这组对象称为测试夹具。

algo.py

  1. def sel_sort(data):
  2. if not isinstance(data, list):
  3. vals = list(data)
  4. else:
  5. vals = data
  6. size = len(vals)
  7. for i in range(0, size):
  8. for j in range(i+1, size):
  9. if vals[j] < vals[i]:
  10. _min = vals[j]
  11. vals[j] = vals[i]
  12. vals[i] = _min
  13. return vals
  14. ...

对于此示例,我们向algo.py模块添加了一个选择排序算法。

fixtures.py

  1. #!/usr/bin/env python3
  2. import algo
  3. import pytest
  4. @pytest.fixture
  5. def data():
  6. return [3, 2, 1, 5, -3, 2, 0, -2, 11, 9]
  7. def test_sel_sort(data):
  8. sorted_vals = algo.sel_sort(data)
  9. assert sorted_vals == sorted(data)

我们用夹具测试选择排序。

  1. @pytest.fixture
  2. def data():
  3. return [3, 2, 1, 5, -3, 2, 0, -2, 11, 9]

我们的测试装置仅返回一些测试数据。 请注意,我们通过其名称引用此灯具:data

  1. def test_sel_sort(data):
  2. sorted_vals = algo.sel_sort(data)
  3. assert sorted_vals == sorted(data)

test_sel_sort()函数中,我们将数据夹具作为函数参数传递。

  1. $ pytest fixtures.py
  2. ================================================= test session starts =================================================
  3. platform win32 -- Python 3.7.0, pytest-5.0.1, py-1.8.0, pluggy-0.12.0
  4. rootdir: C:\Users\Jano\Documents\pyprogs\pytest
  5. collected 1 item
  6. fixtures.py . [100%]
  7. ============================================== 1 passed in 0.02 seconds ===============================================

这是输出。

Pytest 布局

Python 测试可以多种方式组织。 测试可以集成在 Python 包中,也可以放在包外。

综合测试

接下来,我们展示如何在 Python 包中运行测试。

  1. setup.py
  2. utils
  3. algo.py
  4. srel.py
  5. __init__.py
  6. └───tests
  7. algo_test.py
  8. srel_test.py
  9. __init__.py

我们有这种包装布局。 测试与包一起位于tests子目录中。

setup.py

  1. #!/usr/bin/env python3
  2. from setuptools import setup, find_packages
  3. setup(name="utils", packages=find_packages())

这是setup.py

utils/algo.py

  1. def sel_sort(data):
  2. if not isinstance(data, list):
  3. vals = list(data)
  4. else:
  5. vals = data
  6. size = len(vals)
  7. for i in range(0, size):
  8. for j in range(i+1, size):
  9. if vals[j] < vals[i]:
  10. _min = vals[j]
  11. vals[j] = vals[i]
  12. vals[i] = _min
  13. return vals
  14. def max(values):
  15. _max = values[0]
  16. for val in values:
  17. if val > _max:
  18. _max = val
  19. return _max
  20. def min(values):
  21. _min = values[0]
  22. for val in values:
  23. if val < _min:
  24. _min = val
  25. return _min

这是algo.py文件。

utils/srel.py

  1. def is_palindrome(val):
  2. return val == val[::-1]

我们还有另一个模块,其中包含一个测试单词是否为回文的函数。

tests/algo_test.py

  1. #!/usr/bin/env python3
  2. import utils.algo
  3. import pytest
  4. @pytest.fixture
  5. def data():
  6. return [3, 2, 1, 5, -3, 2, 0, -2, 11, 9]
  7. def test_sel_sort(data):
  8. sorted_vals = utils.algo.sel_sort(data)
  9. assert sorted_vals == sorted(data)
  10. def test_min():
  11. values = (2, 3, 1, 4, 6)
  12. val = utils.algo.min(values)
  13. assert val == 1
  14. def test_max():
  15. values = (2, 3, 1, 4, 6)
  16. val = utils.algo.max(values)
  17. assert val == 6

这些是utils.algo模块的测试。 注意,我们使用完整的模块名称。

tests/srel_test.py

  1. #!/usr/bin/env python3
  2. import utils.srel
  3. import pytest
  4. @pytest.mark.parametrize("word, expected", [('kayak', True),
  5. ('civic', True), ('forest', False)])
  6. def test_palindrome(word, expected):
  7. val = utils.srel.is_palindrome(word)
  8. assert val == expected

这是对is_palindrome()函数的测试。

utils/__init__.py

utils/tests/__init__.py

两个__init__.py文件均为空。

  1. $ pytest --pyargs utils
  2. ================================================= test session starts =================================================
  3. platform win32 -- Python 3.7.0, pytest-5.0.1, py-1.8.0, pluggy-0.12.0
  4. rootdir: C:\Users\Jano\Documents\pyprogs\pytest\structure
  5. collected 6 items
  6. utils\tests\algo_test.py ... [ 50%]
  7. utils\tests\srel_test.py ... [100%]
  8. ============================================== 6 passed in 0.06 seconds ===============================================

我们使用pytest --pyargs utils命令运行测试。

外部测试

下一个示例显示了应用源布局,其中测试未集成在包内。

  1. setup.py
  2. src
  3. └───utils
  4. algo.py
  5. srel.py
  6. tests
  7. algo_test.py
  8. srel_test.py

在这种布局中,我们在源代码树之外进行测试。 请注意,不需要__init__.py文件。

  1. $ set PYTHONPATH=src
  2. $ pytest

我们设置PYTHONPATH并运行 Pytest。

在本教程中,我们介绍了 Python Pytest 库。

您可能也对以下相关教程感兴趣: Django 电子邮件教程Python Jinja 教程Python 教程,或列出所有 Python 教程