GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing zetcode.com — Pytest - testing Python applications with the pytest library Pytestlast modified January 29, 2024 Pytest tutorial shows how to test Python application using the pytest module. Python pytestPytest is a Python library for testing Python applications. It is an alternative to nose and unittest. pytest installPytest is installed with the following command: $ pip install pytest
This installs the pytest test discovery conventions
If no arguments are specified then test files are searched in locations from
In the selected directories, pytest looks for Running pytestWith no arguments, pytest looks at the current working directory (or some other preconfigured directory) and all subdirectories for test files and runs the test code it finds. $ pytest Running all test files in the current directory. $ pytest min_max_test.py We can run a specific test file by giving its name as an argument. $ pytest min_max_test.py::test_min
A specific function can be run by providing its name after the $ pytest -m smoke
Markers can be used to group tests. A marked grouped of tests is then
run with $ pytest -k <expression> In addition, we can use expressions to run tests that match names of test functions and classes. Python pytest simple exampleIn the first example, we are going to test two simple math algorithms with pytest. algo.py
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
We have a module with custom min_max_test.py
#!/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
The testing file def test_min(): values = (2, 3, 1, 4, 6) val = algo.min(values) assert val == 1
Also, the testing function $ 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 ===============================================
This is the output. There were two tests and both have successfully passed.
A more verbose output is shown with Pytest skipWith the skip decorator, we can skip the specified tests. There are multiple reasons for skipping test; for instance, a database/online service is not available at the moment or we skip Linux specific tests on Windows. skipping.py
#!/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
In the example, the $ 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 ========================================= In the output following the test file name, s stands for skipped and . for passed. Pytest markingWe can use markers to organize tests into units. marking.py
#!/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'}"
We have two groups of test identified by markers, a and b.
These units are run by Pytest parameterized tests
With parameterized tests, we can add multiple values to
our assertions. We use the parameterized.py
#!/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
In the example, we test the two functions with multiple input data. @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
We pass two values to the test function: the data and the expected
value. In our case, we test the $ 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 output informs that there were six runs. Pytest fixturesTests need to run against the background of a known set of objects. This set of objects is called a test fixture. algo.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
...
For this example, we add a selection sort algorithm to the fixtures.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)
We test the selection sort with a fixture. @pytest.fixture
def data():
return [3, 2, 1, 5, -3, 2, 0, -2, 11, 9]
Our test fixture simply returns some test data. Note that
we refer to this fixture by its name: def test_sel_sort(data): sorted_vals = algo.sel_sort(data) assert sorted_vals == sorted(data)
In the $ 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 layoutsPython tests can be organized in various ways. Tests can be integrated in the Python package or they can rest outside the package. Integrated testsNext we show how to run tests within a Python package. setup.py
utils
│ algo.py
│ srel.py
│ __init__.py
│
└───tests
algo_test.py
srel_test.py
__init__.py
We have this package layout. The tests are located in the setup.py
#!/usr/bin/python from setuptools import setup, find_packages setup(name="utils", packages=find_packages())
This is the utils/algo.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
This is the utils/srel.py
def is_palindrome(val):
return val == val[::-1]
We have another module, which contains a function to test whether a word is a palindrome. tests/algo_test.py
#!/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
These are the tests for the tests/srel_test.py
#!/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
This is a test for the utils/__init__.py
utils/tests/__init__.py
Both $ 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 ===============================================
We run the tests with Tests outside the packageThe next example shows an application source layout where the tests are not integrated inside the package. setup.py
src
└───utils
│ algo.py
│ srel.py
tests
algo_test.py
srel_test.py
In this layout, we have tests outside the source tree.
Notice that the $ set PYTHONPATH=src $ pytest
We set the SourceIn this article we have introduced the Python pytest library. AuthorMy name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming. List all Python tutorials. |
| You are visitor 01402520 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |