Functional Testing Practical#

With this short tutorial, we’ll learn how to create some “functional tests” using a mock up of some production code.

Why This Matters#

We often work with large code bases, and ensuring that each individual piece is working is difficult. By isolating individual parts of the code base, while still utilizing the codebase, we can exercise and test parts of the code, knowing that we are testing the actual production code.

Unlike unit tests, functional tests don’t necessarily have a pass/fail state. Functional tests can be used to explore output from a method given known input values, or a range of possible values.

These tests can help with identifying code bugs and errors, or in exploring new ways of representing our science.

What We’ll Do#

We will work through a few examples of creating functional tests for a mock up of a python library intended to simulate fire behavior. This code has been translated from the Fortran “SPITFIRE” code in the FATES repository for ease of use in this tutorial. However, the concepts here are directly applicable to the original code base (or other large Fortran codebases).

We will start with a basic example of utilizing and exercising a method from the source code. Gradually, we will explore more complex uses of the methods in the source code, investigating using synthetic fuels and comparing to and driving with known observational data.

Source Code#

The source code we will be using is a handful of methods from the SPITFIRE modules of the Fortran-based FATES repository. FATES (the Functionally Assembled Terrestrial Ecosystem Simulator) is FATES is a vegetation demographic model which can be run within a “host” land surface model like the Community Land Model (CLM). These methods have been translated to Python for ease of use for this tutorial.

FATES can represent physiological and demographic processes for individual “cohorts” of plants of similar size and PFT that exist on landscape “patches”, which are fractions of the grid cell with similar time-since-disturbance. The fire modules within FATES are used to calculate fire weather, fuel conditions, fire risk, fire behavior, and fire mortality of plants. These methods are very important for accurately predicting wildfires as well as overal vegetation and carbon dynamics.

As such, we should be testing them!

Accessing and Setting Up Tutorial Library#

1. Clone the repository#

Open a terminal and run:

git clone https://github.com/adrifoster/functional_testing_tutorial
cd functional_testing_tutorial

Your repository should have this structure:

functional_testing_tutorial/
├─ src/
|  └── fire_code/  
│     ├─ __init__.py
│     ├─ fire_equations.py
│     ├─ fire_params.py
│     ├─ fire_weather_class.py
│     ├─ fuel_class.py
│     ├─ fuel_types.py
│     ├─ main_model.py 
│     ├─ nesterov_fire_weather.py
│     └─ testing/
│        ├─ __init__.py
│        ├─ synthetic_fuel_models.py
│        └─ testing_shr.py
├─ notebooks/
├─ data/
├─ figs/
├─ parameter_files/
├─ setup.py
├─ README.md
├─ LICENSE
├─ environment.yml
└─ requirements.txt

2. Create Python environment#

Option B: Pip (if not using Conda)#

In a terminal window type:

python -m venv .venv
source .venv/bin/activate       # macOS/Linux
.venv\Scripts\activate          # Windows

pip install --upgrade pip
pip install -r requirements.txt

3. Install tutorial library#

Once your environment is active, install the Python package in editable mode:

pip install -e .

This makes functional_testing_tutorial importable anywhere in the environment and allows changes to src/ to take effect immediately.

Test the installation#

python -c "from fire_model.fire_equations import FireEquations; print('Library installed!')"

If you see Library installed!, you’re good to go!

Check Out the Source Code#

Let’s look through our source code:

  • fire_equations.py: this is a FireEquations class that holds static methods for fire behavior

  • fire_params.py: holds a FireParams data class that deals with parameters related to the fire model

  • fire_weather_class.py: an abstract FireWeather class, which holds values and methods related to fire weather

  • nesterov_fire_weather.py: a concrete FireWeather class

  • fuel_class.py: holds the Fuel class, which holds various fuel attributes and methods

  • fuel_types.py: an enumerator class that deals with the indices related to fuel types

  • main_model.py: holds main functions for the fire model

  • testing/synthetic_fuel_models.py: holds classes related to mock-ups of our Fuel class

  • testing/testing_shr.py: holds helper functions for testing

Feel free to look through the source code files to gain a little familiarity with the methods, though this is not necessary. We will go over them in later notebooks.