Integration fixtures
Because we always need Docker to run the Postgres integration test, we can create a pytest fixture to handle the management of Docker. We will take the CLI command we used before and write a fixture around them.
@pytest.fixture(scope="session", autouse=True)
def docker_compose():
# Start Docker Compose
file_path = Path(__file__).absolute().parent / "docker-compose.yaml"
subprocess.run(
["docker", "compose", "-f", file_path, "up", "--build", "-d"],
check=True,
capture_output=True,
)
max_retries = 5
for i in range(max_retries):
result = subprocess.run(
["docker", "exec", "postgresql", "pg_isready"],
capture_output=True,
)
if result.returncode == 0:
break
time.sleep(5)
yield
# Tear down Docker Compose
subprocess.run(["docker", "compose", "-f", file_path, "down"], check=True)
The code above does the following:
- Gets the file path of the
docker-compose.yaml
relative to the test (which is in the same directory). - Starts the Docker service using the
docker compose
command. - Waits for the service to be ready by checking when the database is ready using
pg_isready
. - Yields to the test execution (running the test).
- Spins down the Docker service when testing has completed.
Now that fixture can be used within the parameters of the integration test and couple our test and the underlying services:
def test_state_population_database(docker_compose): # noqa: F811
postgres_resource = PostgresResource(
host="localhost",
user="test_user",
password="test_pass",
database="test_db",
)
result = lesson_5.state_population_database(postgres_resource)
assert result == [
("New York", 8804190),
("Buffalo", 278349),
]
> pytest dagster_testing_tests/test_lesson_5.py::test_state_population_database
...
dagster_testing_tests/test_lesson_5.py . [100%]
Because this pytest fixture is scoped to the session, it will remain active for all the tests run. This can be very helpful because you may notice these tests take longer than the unit tests. Most of this is the set up time to spin up the necessary services (the tests themselves are relatively quick).
Scoping everything to the session ensures that we do not have to needlessly spin the services up and down for each test.