Tutorials

Getting started with pytest_container

Note

The following tutorial demonstrates how to use pytest_container for a Python project and to test the project’s Command-line interface.

The example uses poetry to manage the Python dependencies. If the pyproject.toml doesn’t already exist, create it using the poetry init command.

Start by adding pytest_container as a development dependency:

❯ poetry add --group dev pytest_container

Add pytest-xdist, so that tests can be executed in parallel:

❯ poetry add --group dev pytest-xdist

It is recommended to create a new directory tests for your tests to keep things tidy. Add an empty file called tests/__init__.py. Create the tests/conftest.py file with the following contents:

from pytest_container import auto_container_parametrize


def pytest_generate_tests(metafunc):
    auto_container_parametrize(metafunc)

The above code snippet ensures that the auto_container and auto_container_per_test fixtures work correctly (that is, they use the images defined in CONTAINER_IMAGES).

We can now implement the actual tests. We will create a simple smoke test where we install the python wheel of our project inside a container and then check that the installed command line utility works as expected. For that, we will start out with the following Dockerfile. There we base our image on openSUSE Tumbleweed, install pip into it, copy the wheel of our python project into the image and install it in the container:

FROM registry.opensuse.org/opensuse/tumbleweed

RUN zypper -n in python3-pip
COPY dist/*whl .
RUN pip install *whl

Plug the Dockerfile into pytest_container using the DerivedContainer class as follows:

from textwrap import dedent
from pytest_container import DerivedContainer


TW_WITH_PKG = DerivedContainer(
    base="registry.opensuse.org/opensuse/tumbleweed",
    containerfile=dedent("""
    RUN zypper -n in python3-pip
    COPY dist/*whl .
    RUN pip install *whl
    """)
)

Note that the FROM line is omitted from the containerfile parameter, as pytest_container includes it automatically.

Add the above snippet to a new file tests/test_cli.py, and add the following test function along with a global variable:

CONTAINER_IMAGES = [TW_WITH_PKG]

def test_help_works(auto_container):
    res = auto_container.connection.run_expect([0], "my-binary --help")
    assert "My cool project" in res.stdout

The global variable CONTAINER_IMAGES instructs pytest_container to run all test functions that use the auto_container or auto_container_per_test fixtures once for each image defined in that list. This allows you to have a single file with multiple of tests that to be executed inside multiple container images, thus avoiding the task of parametrizing each of the test manually.

The test function receives a ContainerData instance, where the connection attribute provides a testinfra connection. The run_expect function is used to execute the binary and check that its exit code is 0. Afterwards, we check that a search string is in the standard output.

You can now execute this test via poetry run pytest.