MongoDB.local SF, Jan 15: See the speaker lineup & ship your AI vision faster. Use WEB50 to save 50%
Find out more >
Docs Menu
Docs Home
/ /

Tutorial: Test and Package a Python Library

This tutorial shows how to add pytest fixtures that run tests against MongoDB and use transactions to roll back changes. It also shows how to package a Python library by using the hatchling library and publish the package to PyPI.

Tip

Coding with Mark

This tutorial is based on the second episode of the "Coding with Mark" livestream. You can watch the recording on the MongoDB YouTube channel.

Fixtures in pytest are functions that create resources for use in unit tests. Typically, fixtures are defined in a file named conftest.py in your project directory. You can use a yield statement to return a value to the test function.

Each fixture has a scope, which controls how often the fixture runs. The following fixture, sample_fixture, uses the default scope, function. This means that pytest runs the fixture before each test function that uses it. The fixture uses a yield statement to return the string "Hello, World" to the test function.

def sample_fixture():
# Code before the test runs
yield "Hello, World"
# Code after the test runs

Before you begin the tutorial, complete the following steps:

  1. Follow the steps in the Get Started guide to install PyMongo and its dependencies. Be sure to copy your connection string for use in a later step.

  2. Run the following command to install pytest in your environment:

    pip install -U pytest
  3. Find the MongoDB connection string that you copied in a previous step. Run the following command to store the connection string in the MDB_URI environment variable:

    export MDB_URI="<your connection string>"
  4. Install the build and twine packages to package and publish your library. The build package installs build dependencies and packages your project, and the twine package securely uploads your package to PyPI.

    python -m pip install --upgrade build twine

This section of the tutorial includes the following tasks:

  1. Adding pytest fixtures for a MongoClient object and a rollback session

  2. Writing tests that use these fixtures

1

Create a new directory for your project and navigate to it in your terminal:

mkdir sample_application
cd sample_application
2

The following code sample shows a fixture named mongodb. This fixture uses the connection string in the MDB_URI environment variable to create a MongoClient object, then ensures that it can connect to the specified MongoDB deployment. The fixture has a session scope, which means that pytest runs the fixture at the start of the test session and reuses it for all tests.

This fixture uses the mongodb fixture by including it as a function parameter. This instructs pytest to pass mongodb as an argument to the fixture.

Add the following code to a file named conftest.py:

import pytest
import pymongo
import os
@pytest.fixture(scope="session")
def mongodb():
client = pymongo.MongoClient(os.environ["MDB_URI"])
assert client.admin.command("ping")["ok"] != 0.0
yield client
client.close()

To test the fixture, add the following code to a file named test_mongodb_fixture.py:

def test_mongodb_fixture(mongodb):
"""This test passes if `MDB_URI` is set to a valid connection
string."""
assert mongodb.admin.command("ping")["ok"] > 0

Run the test by executing the following command in your terminal:

pytest test_mongodb_fixture.py

If the test is successful, it prints a message similar to the following:

============================= test session starts ==============================
collected 1 item
test_mongodb_fixture.py . [100%]
============================== 1 passed in 0.XXs ===============================
3

If your tests modify data in MongoDB, but you don't want those changes to persist, you can use a transaction to roll back the changes after each test. To do so, perform the following steps in your fixture:

  1. Create a session

  2. Start a transaction

  3. Yield the session to the test

  4. Abort the transaction after the test runs

The fixture in the following code example implements this pattern:

@pytest.fixture
def rollback_session(mongodb):
session = mongodb.start_session()
session.start_transaction()
try:
yield session
finally:
session.abort_transaction()

The preceding fixture uses the default function scope, so abort_transaction runs after each test function.

To test the fixture, add the following code to a file named test_update_mongodb.py:

def test_update_mongodb(mongodb, rollback_session):
my_collection = mongodb["sample_db"]["sample_collection"]
my_collection.insert_one(
{
"_id": "bad_document",
"description": (
"If this still exists, then transactions are not working."
),
},
session=rollback_session,
)
assert (
my_collection.find_one(
{"_id": "bad_document"}, session=rollback_session
)
is not None
)

Note

session Argument

Pass the session argument to all queries. This ensures that they are part of the transaction.

Run the test by executing the following command in your terminal:

pytest test_update_mongodb.py

If the test is successful, it prints a message similar to the following:

============================= test session starts ==============================
collected 1 item
test_update_mongodb.py . [100%]
============================== 1 passed in 0.XXs ===============================

In this section, you can learn how to package a sample Python library by performing the following actions:

  1. Describe the package

  2. Build the package

  3. Publish the package to PyPI

1

Describe your project in a file named pyproject.toml. The following file describes a package that uses the hatchling build backend:

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "sample_application"
version = "0.0.1"
authors = [{ name = "<your name>", email = "<your email address>" }]
description = "An example application to demonstrate packaging a Python project."
requires-python = ">=3.10"
dependencies = ["pymongo>=4.15.5"]
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: Apache Software License",
"Operating System :: OS Independent",
"Development Status :: 1 - Planning",
"Intended Audience :: Developers",
"Topic :: Database",
]
[tool.hatch.build.targets.wheel]
packages = ["sample_application"]
[project.urls]
Homepage = "https://github.com/mongodb"

Tip

More pyproject Settings

For more information about writing a pyproject.toml file, see Writing your pyproject.toml.

2

To build a distribution from your package, run the following command in your project directory:

python3 -m build

The preceding step creates a wheel and gzipped tarball for your package. If the build operation is successful, it prints output similar to the following:

* Creating isolated environment: venv+pip...
* Installing packages in isolated environment:
- hatchling
* Getting build dependencies for sdist...
* Building sdist...
* Building wheel from sdist
* Creating isolated environment: venv+pip...
* Installing packages in isolated environment:
- hatchling
* Getting build dependencies for wheel...
* Building wheel...
Successfully built sample_application-0.0.1.tar.gz and sample_application-0.0.1-py3-none-any.whl
3

After building, you can upload your wheel and gzipped tarball by using twine. To upload these files, run the following command in your project directory:

python3 -m twine upload dist/*

A successful upload produces output similar to the following messages:

* Uploading distributions to https://upload.pypi.org/legacy/
* Enter your username:
* Enter your password:
* Uploading sample_application-0.0.1-py3-none-any.whl
* Uploading sample_application-0.0.1.tar.gz
* View at: https://pypi.org/project/sample_application/0.0.1/

For more information about the Python packages and code used in this tutorial, see the following resources:

Back

Tutorial: FastAPI Integration

On this page