Continuous Integration
Unit testing is one of the most useful practices you can incorporate to improve your code’s quality. Testing is so important that it is best to automate the process so your tests run every time changes are made. This way, if something breaks, you can identify exactly when the code stopped working and get to the bottom of it more easily.
Testing python code
.github/workflows/build-python.yml
name: build python package
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
1 strategy:
matrix:
2 python-version: ["3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
3 with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install ./python-package[test] --upgrade pip
- name: Test
run: |
python -m pytest- 1
- Use a matrix strategy to run the job multiple times
- 2
- The build job will run once for Python version 3.11 and once for version 3.12
- 3
-
with:allows you to define variables that are used by the action. The Python version from the matrix is passed along to thesetup-pythonaction.
Add this workflow and the example code in python-package to a new branch in your repo. Push your branch and open a pull request (PR). The workflow will begin running for your latest commit in the PR branch.
When the workflow run fails

Take a look at the workflow status: a green check ✅ means it passed and a red X ❌ means it failed. Let’s investigate the logs to find out why it failed. Click on one of the build jobs and take a look at the pytest step.

The output of pytest shows that one of our tests failed. Does it also fail when we run pytest locally?
# first install the local version of the package
pip install -e ./python-package
# then try running pytest
python -m pytest============================= test session starts ==============================
platform darwin -- Python 3.11.9, pytest-8.3.2, pluggy-1.5.0
rootdir: /Users/sovacoolkl/projects/CCBR/gh-actions-sandbox
plugins: cov-5.0.0, anyio-4.4.0
collected 2 items
python-package/tests/test_main.py .F [100%]
=================================== FAILURES ===================================
___________________________________ test_add ___________________________________
def test_add():
> assert add(2, 3) == 5
E assert -1 == 5
E + where -1 = add(2, 3)
python-package/tests/test_main.py:7: AssertionError
=========================== short test summary info ============================
FAILED python-package/tests/test_main.py::test_add - assert -1 == 5
========================= 1 failed, 1 passed in 0.03s ==========================Oops, we seem to have a bug in our code! Edit python-package/src/mypkg/main.py and fix the bug.
There’s something wrong with the add() function.
After you fix the bug and save the source file, try running pytest to make sure it’s really fixed this time:
python -m pytest============================= test session starts ==============================
platform darwin -- Python 3.11.9, pytest-8.3.2, pluggy-1.5.0
rootdir: /Users/sovacoolkl/projects/CCBR/gh-actions-sandbox
plugins: cov-5.0.0, anyio-4.4.0
collected 2 items
python-package/tests/test_main.py .. [100%]
============================== 2 passed in 0.02s ===============================Great, all the tests passed! Now we can commit and push the fix to your branch. Notice how the workflow re-runs when you push the latest commit. Does the workflow complete successfully this time?
You can setup a branch protection rule1 to require that this workflow passes before Pull Requests can be merged into your default branch. This way, you can guarantee that all contributions pass the unit tests.
More CI workflows
Take a look at these full-featured examples for testing Python and R packages: