pytest Tutorial – Part 1

At PyConDE Berlin 2019 Florian Bruhin gave a really nice session about testing with pytest, which I try to recap here.

Writing Tests

If You want to work with pytest you can install it via:

pip install pytest

When you know basic python unittest fixtures, good news ahead:

pytest is compatible and will run your old unittest classes as well, but pytest doesn’t need to have a testclass derived from some subclass.

It is sufficient to have a single python file which name starts with test_ containing a function which name also starts with test_

# test_stuff.py
def test_stuff():
    x = 5.0
    assert x == 5

That’s all You need!

Autodiscovery

If you run pytest from the command line it will automatically discover tests which cohere to this naming convention like unittest -discover

pytest

Select test cases

You can specify the tests to be run by module::test_function or by module::test_class::test_method e.g.

pytest test_sort_algortihms.py::TestSortAlgorithms::test_python_sorted

Execution time

If you pass --durations=0 execution time for all tests will be reported.

Useful command line arguments

When learning pytest, it is good to learn some command line flags as well

  • -h : help
  • -x : quit on first error
  • –pdb : start debugger on error
  • -v : increase verbosity
  • -s : show print output
  • -rf : reason for failure
  • -rs : reason for skipping
  • -m <marker_name> : run only marked tests
  • –fixtures : show fixtures in modules

Configuration via file

You can save your preferences for running pytest in a file called pytest.ini

[pytest]
addopts = -x --durations=0 -vv

Understanding Fixtures

Fixtures are pytest’s equivalent to setUp methods from unittest with the difference that you can define when they should be used.

import pytest

@pytest.fixture
def some_value():
    return 42

def test_function(some_value):
    assert some_value == 42

After declaring a fixture with decorator @pytest.fixture you can use this special function as a parameter to your test functions.

Fixture scope

Without any further definition a fixture function is called before every function it is used in. When You do some heavy lifting in a fixture and you want to reuse that you can specify the module scope so that the fixture function just runs once per module

@pytest.fixture(scope="module")

If you even want to instantiate a fixture once per session this is also possible with

@pytest.fixture(scope="session)