Test Python Applications with Oracle Database Free using Testcontainers

Testcontainers is a wildly popular library that provides developers with on-demand, disposable containers.

When you build apps that use databases like Oracle, testing against a real instance instead of mocks or in-memory alternatives improves reliability and confidence in application behavior.

In this article, we’ll use the Testcontainers Python binding with Oracle Database Free to spin up disposable database containers – containers you can use for automated testing and experimentation.

Just looking for the code sample? Click Here.

Dependencies

We’ll use the python-oracledb and testcontainers-python projects to implement Testcontainers support for Oracle Database Free. If you’re declaring your Python dependencies in the pyproject.toml format, it’ll look like this:

dependencies = [
    "oracledb (>=3.3.0,<4.0.0)",
    "testcontainers (>=4.13.0,<5.0.0)"
]

Or, with pip:

pip install oracledb
pip install testcontainers

Oracle Database implementation

Unfortunately, the existing Oracle Database implementation for Testcontainers uses the deprecated Oracle_Cx Python driver, and is only compatible with older container images.

However, it’s easy to create our own implementation that uses the gvenzl/oracle-free 23ai database container images.

The following Python class inherits Testcontainer’s DbContainer class to implement an OracleDatabaseContainer. This class can be used by Testcontainers to spin up and connect to disposable Oracle Database containers:

import oracledb
from testcontainers.core.generic import DbContainer

# OracleDatabaseContainer implements a Testcontainers DbContainer for the
# gvenzl/oracle-free image variants.
class OracleDatabaseContainer(DbContainer):
    def __init__(self,
                 app_user,
                 app_user_password,
                 image="gvenzl/oracle-free:23.26.0-slim-faststart",
                 db_name="freepdb1",
                 container_port=1521,
                 host="localhost",
                 **kwargs):
        super(OracleDatabaseContainer, self).__init__(image=image, **kwargs)
        self.container_port = container_port
        self.app_user = app_user
        self.app_user_password = app_user_password
        self.db_name=db_name
        self.host = host

        self.with_env("ORACLE_RANDOM_PASSWORD", "y")
        self.with_env("APP_USER", app_user)
        self.with_env("APP_USER_PASSWORD", app_user_password)
        self.with_exposed_ports(container_port)

    def get_connection(self):
        bind_port = self.get_exposed_port(self.container_port)
        return oracledb.connect(user=self.app_user, password=self.app_user_password,
                                host=self.host, port=bind_port, service_name=self.db_name)

    def get_connection_url(self):
        bind_port = self.get_exposed_port(self.container_port)
        return f'oracle+oracledb://{self.app_user}:{self.app_user_password}@{self.host}:{bind_port}/?service_name={self.db_name}'

    def _configure(self):
        pass

Sample Program

Using the OracleDatabaseContainer class, we’ll write a sample program that starts a container and queries the database version.

# import the class we just implemented
# you may need to modify this import path depending on your project
from src.python_oracle.testcontainers_sample.oracle_database_container import OracleDatabaseContainer


def testcontainers_example():
    app_user = "testuser"
    app_password = "testpwd12345"

    # Start the container and try to query the database version
    with OracleDatabaseContainer(
            app_user=app_user,
            app_user_password=app_password
    ) as oracledb:
        cursor = oracledb.get_connection().cursor()
        for row in cursor.execute("select * from V$VERSION"):
            if row is None:
                print("No result from query!")
            print(row)

if __name__ == "__main__":
    testcontainers_example()

If you download and run the sample using poetry, you should see something similar to the following output – indicating the container image was pulled, started, and the version query ran successfully:

Pulling image gvenzl/oracle-free:23.9-slim-faststart
Container started: 164861e88ad6
Waiting to be ready...
Waiting to be ready...
Waiting to be ready...
Waiting to be ready...
Waiting to be ready...
Waiting to be ready...
Waiting to be ready...
('Oracle Database 23ai Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free', 'Oracle Database 23ai Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free\nVersion 23.9.0.25.07', 'Oracle Database 23ai Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free', 0)

This code can easily be used for testing, experimentation, and automating unit or integration tests.

Testcontainers with Oracle Database also works great in Java – if you’re a Java developer, check out my article Easily test Oracle Database applications with Testcontainers.

References

Response

  1. […] Test Python Applications with Oracle Database Free using Testcontainers – Anders Swanson shows how testing Python applications with Oracle Database Free is easy […]

Leave a Reply

Discover more from andersswanson.dev

Subscribe now to keep reading and get access to the full archive.

Continue reading