Skip to Content
Course content

342: Continuous Integration for a Python Test Suite

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Up until now, you've been running your tests manually. You write a feature, run pytest, see the green dots, and push to your repository. That works fine for a solo project, but the moment you collaborate with anyone else—or even just move between a laptop and a desktop—you'll realize that "it worked on my machine" is a dangerous phrase. That's where Continuous Integration (CI) comes in. It's basically a robot that runs your tests every single time you push code, ensuring you didn't accidentally break something in the process.

Our Target: A Simple Currency Converter

To keep this concrete, let's imagine we're building a small library called fx_calc that handles currency conversions. I've already got a basic function that takes an amount and a rate, and a corresponding test suite. Here is the structure of the project I'm working with:

fx_calc/
├── fx_calc/
│   └── converter.py
├── tests/
│   └── test_converter.py
└── requirements.txt

The converter.py file is simple, but the test_converter.py file is where the magic happens. I'm using pytest because it's the industry standard for a reason—it's less boilerplate than unittest. Now, I want to make sure that whenever I push a change to GitHub, these tests run automatically.

Automating the Boring Stuff with GitHub Actions

I'm using GitHub Actions for this because it's integrated right into the repo. To get started, I need to create a specific directory structure: .github/workflows/. Inside that, I'll create a file called ci.yml. This YAML file is essentially a recipe that tells GitHub, "Here is the environment I need, and here are the commands I want you to run."

Here is my first attempt at the configuration:

name: Python CI

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.10'
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
      - name: Run tests
        run: pytest

Wait, Why Did the Build Fail?

I pushed the code, felt confident, and then... a red "X". The build failed. I checked the logs, and I saw a classic ImportError: No module named 'fx_calc'.

Here's what happened: I told the CI robot to install the dependencies listed in requirements.txt (which includes pytest), but I forgot that the fx_calc code itself isn't a "dependency" in that file—it's the project I'm currently building. When pytest runs in the GitHub environment, it doesn't automatically know that the root folder is a package it should be looking at.

I've made this mistake a dozen times. I was thinking about the environment in terms of "what libraries do I need?" rather than "how does Python find my local code?".

Getting the Green Checkmark

To fix this, I have two choices: I could mess around with PYTHONPATH, or I could do it the professional way and install my current project in "editable" mode. By adding pip install -e . to my installation step, I tell Python to treat the current directory as an installed package.

I updated the "Install dependencies" section of my ci.yml to look like this:

      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
          pip install -e .

I pushed the change, waited about 40 seconds, and there it was: the beautiful green checkmark. Now, if I accidentally change a formula in converter.py that breaks my tests, GitHub will scream at me before that code ever makes it into the main branch. That's the peace of mind CI provides.




📋 Practical Task

Implementing a CI Pipeline for a Markdown-to-HTML Parser

You have been handed a small project called md_parse. It contains a function that converts basic Markdown strings to HTML. The project structure is as follows:

md_parse/
├── md_parse/
│   └── parser.py
├── tests/
│   └── test_parser.py
└── requirements.txt

The requirements.txt file already contains pytest. Your task is to create a GitHub Actions workflow file (.github/workflows/ci.yml) that:

  • Triggers on every push and pull_request.
  • Uses ubuntu-latest as the runner.
  • Sets up Python version 3.11.
  • Installs the dependencies from requirements.txt.
  • Installs the md_parse project itself so that the tests can import it.
  • Executes the test suite using pytest.

Write the complete YAML configuration for this workflow.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.