Pytest Testing Idioms

From NovaOrdis Knowledge Base
Jump to navigation Jump to search

External

Internal

Overview

Test files start with test_ (?) A function that starts with test is automatically considered a test to be executed. A function that does NOT start with test is ignored as test, so if one tries to specifically execute it, they get:

ERROR: not found: /Users/ovidiu/src/.../test_template.py::null_template_variables

A test function can be injected with a test fixture: Available fixtures: cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory.

Tested Code is Supposed to Throw Exception

import pytest

def test_something_that_should_throw_exception():
    with pytest.raises(Exception) as info:
        my_method()

If the tested code raises a more specific exception, you can use that instead:

def test_something_that_should_throw_exception():
    with pytest.raises(ValueError) as info:
        my_method()

This tests the exception arguments. ⚠️ In this case, it is very import to place the assert statements outside the with block, otherwise they will be silently ignored.

def test_something_that_should_throw_exception():
    with pytest.raises(ValueError) as info:
        my_method()
    # important: interaction with 'info' must take place outside the with block
    assert info.value.args[0] == "some message"
    assert "some message" in str(info.value)

Also see:

Python Language | Exceptions

xunit-Style Setup

https://docs.pytest.org/en/7.1.x/how-to/xunit_setup.html
def setup_function(function):
    """setup any state tied to the execution of the given function.
    Invoked for every test function in the module.

    'function' is optional.
    """
    Path(Path(__file__).parent, './data/tmp').mkdir()


def teardown_function(function):
    """teardown any state that was previously setup with a setup_function
    call.

    'function' is optional.
    """
    shutil.rmtree(str(Path(Path(__file__).parent, './data/tmp/')))

Failing a Test

from _pytest.outcomes import fail

def test_something():
  fail("this test failed because I wanted it to")

Mocking

Mocking with unittest

Hierarchical Tests

Find a solution for hierarchical tests for class hierarchies: the test hierarchy should match the class hierarchy and each layer in the hierarchy should test the behavior implemented by its corresponding class hierarchy level. This works well in Java and Junit.


Skipping Tests in Python

from unittest import skip
@skip(reason="TODO 23jLP")
def test_something():
    [...]


@pytest.mark.skip(reason="TODO 23jLP")
def test_something():
    [...]