Testing

Objectives

  • Know where to start in your own project.

  • Have an example for how to make the testing part of code review.

Motivation

Testing is a way to check that the code does what it is expected to.

  • Less scary to change code: tests will tell you whether something broke.

  • Easier for new people to join.

  • Easier for somebody to revive an old code.

  • End-to-end test: run the whole code and compare result to a reference.

  • Unit tests can guide towards better structured code: complicated code is more difficult to test.

Where to start

Short answer: Start with an end-to-end test.

Demonstration

Discussion

Is the end-to-end test perfect? No. But it’s a good starting point. Discuss its limitations.

Where to explore more

Exercises

Exercise Testing-1: Design an end-to-end test

Try this process on your own code or a code that you have recently used:

  1. Describe in words how you check whether the code still works.

  2. Translate the words into a script (any language).

  3. Run the script automatically on every code change.

Already step 1 is a good exercise in itself. If you can arrive at step 2, it’s amazing, but it can take some time. Step 3 is typically relatively easy.

Exercise Testing-2: Add a unit test

You can do this exercise either on your computer (in this case clone the example project), or on GitHub (in this case fork the example project).

First we need to add a test function, for instance for this function:

def force_between_planets(position1, mass1, position2, mass2):
    G = 1.0  # gravitational constant

    r = position2 - position1
    distance = (r[0] ** 2 + r[1] ** 2 + r[2] ** 2) ** 0.5
    force_magnitude = G * mass1 * mass2 / distance**2
    force = (r / distance) * force_magnitude

    return force


def test_force_between_planets():
    position1 = np.array([0.0, 0.0, 0.0])
    mass1 = 1.0
    position2 = np.array([1.0, 0.0, 0.0])
    mass2 = 2.0

    force = force_between_planets(position1, mass1, position2, mass2)

    assert np.allclose(force, [2.0, 0.0, 0.0])

We also need to extend our environment.yml:

name: planets
channels:
  - conda-forge
dependencies:
  - python=3.12
  - numpy
  - click
  - matplotlib
  - pytest

If you try this on your computer, you can run the test with:

$ pytest simulate.py

If you want GitHub to run it, we need to extend .github/workflows/test.yml (highlighted line):

name: Test

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout
      uses: actions/checkout@v4

    - uses: mamba-org/setup-micromamba@v1
      with:
        micromamba-version: '1.5.8-0' # any version from https://github.com/mamba-org/micromamba-releases
        environment-file: environment.yml
        init-shell: bash
        cache-environment: true
        post-cleanup: 'all'
        generate-run-shell: false

    - name: Run tests
      run: |
        ./test.sh
        pytest simulate.py
      shell: bash -el {0}