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 A: Conda (recommended)#
In a terminal window type:
conda env create -f environment.yml
conda activate functional-testing-tutorial
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 aFireEquationsclass that holds static methods for fire behaviorfire_params.py: holds aFireParamsdata class that deals with parameters related to the fire modelfire_weather_class.py: an abstractFireWeatherclass, which holds values and methods related to fire weathernesterov_fire_weather.py: a concreteFireWeatherclassfuel_class.py: holds theFuelclass, which holds various fuel attributes and methodsfuel_types.py: an enumerator class that deals with the indices related to fuel typesmain_model.py: holds main functions for the fire modeltesting/synthetic_fuel_models.py: holds classes related to mock-ups of ourFuelclasstesting/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.