Have you ever encountered the frustrating ‘It works on my machine’ problem? This is a common headache in software development. Docker containers provide a solution by packaging your code in a consistent environment. Because of this, Docker has become popular in many areas, especially for automating CI/CD pipelines. In this post, I’ll show you how to use Docker to set up and run a Python testing environment.
You can check out the source code from this Github repository.
Task Description
In this task, we will test a function that utilizes pendulum
to compute the difference between two dates. To proceed with this task, you should:
- have Docker installed on your machine,
- understand how to use
poetry
for Python package management; refer to this post for detailed instructions, - know how to use
pytest
to test code; refer to this post for detailed instructions,
Writing the Function and Tests
The function we are going to test is calculate_date_difference
, and it should be located in the module src/calculate_date_difference.py
. Here’s the definition of the function:
import pendulum
def calculate_date_difference(
start_date: pendulum.Date, end_date: pendulum.Date
) -> int:
difference = end_date - start_date
return difference.days
The test cases are defined within the module tests/test_calculate_date_difference.py
. You can find the test case definitions below. Additionally, remember to include an empty __init__.py
file in the tests directory to enable running pytest tests/
from the current directory.
import pendulum
import pytest
from src.calculate_date_difference import calculate_date_difference
@pytest.mark.parametrize(
"date_1, date_2, expected",
[
((2023, 1, 1), (2023, 1, 10), 9),
((2023, 1, 1), (2023, 1, 1), 0),
((2023, 1, 10), (2023, 1, 1), -9),
],
)
def test_calculate_date_difference(date_1, date_2, expected):
start_date = pendulum.datetime(*date_1)
end_date = pendulum.datetime(*date_2)
assert calculate_date_difference(start_date, end_date) == expected
And your pyproject.toml
file should appear as follows:
[tool.poetry]
name = "pendulum-example"
version = "0.1.0"
description = ""
authors = ["Your Name <[email protected]>"]
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.10"
pendulum = "^2.1.2"
[tool.poetry.group.test.dependencies]
pytest = "^7.4.2"
[tool.poetry.group.dev.dependencies]
ipykernel = "^6.25.2"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
Building the Docker Image via Dockerfile
In this section, we will generate a Docker image that includes all the essential dependencies for running the tests. To achieve this, we will create a file named Dockerfile
within the project’s root directory. This file should consist of the following instructions:
# A
FROM python:3.10.13-slim
# B
ENV PYTHONUNBUFFERED=1
PYTHONDONTWRITEBYTECODE=1
PIP_NO_CACHE_DIR=off
POETRY_VIRTUALENVS_CREATE=false
# C
COPY src/ src/
COPY tests/ tests/
COPY poetry.lock pyproject.toml ./
# D
RUN pip install -U pip &&
pip install poetry==1.5.1 &&
poetry install --no-interaction --no-cache --without dev
# E
ENTRYPOINT ["pytest", "-s", "tests"]
Let’s go through the instructions one by one:
A. From Instruction
The FROM
instruction sets the base image to the official Python slim
image, which is a lightweight version suitable for production use. Ensure the Python version matches that specified in pyproject.toml
.
B. ENV Instruction
The ENV
instruction defines environment variables in the image:
PYTHONUNBUFFERED=1
disables output buffering, enabling real-time application output.POETRY_VIRTUALENVS_CREATE=false
prevents Poetry from creating a virtual environment.PYTHONDONTWRITEBYTECODE=1
stops Python from writing .pyc files to disk.PIP_NO_CACHE_DIR=off
deactivates the pip cache to reduce image size.
C. COPY Instruction
The COPY
instruction transfers the source code, tests, poetry.lock
, and pyproject.toml
files to the image, similar to the cp
command in Linux.
D. RUN Instruction
The RUN
instruction executes commands within the image. Here, we install dependencies using Poetry with specific flags:
--no-interaction
prevents user input prompts.--no-cache
avoids using the cache.--without dev
excludes dev group dependencies, unnecessary for running tests.
E. ENTRYPOINT Instruction
The ENTRYPOINT
instruction defines the command to execute when the container launches. Here, we run the tests using pytest with the -s
flag to display the test output.
Building and Running the Docker Image
To build the Docker image, execute the following command in the project’s current directory. The -t
flag lets you assign a name to the image, and the .
denotes that we’re using the Dockerfile in the current directory. Once the image is built, you can use docker images
to check if the image has been successfully created.
docker build -t pytest_example .
docker images
You can execute the tests using the following command. The --rm
flag ensures the container is removed after it finishes, and the test results will be displayed in the output.
docker run --rm pytest_example
Leave a Reply