Unit Testing in QuantEcon
Summary
The QuantEcon package uses pytest to manage tests. We adhere to some conventions to facilitate ease of maintenance and management of test cases. The main conventions are as follows:
- Tests live in Python files, which in turn live in
quantecon/tests/ - These test files should be prepended with
test_followed by module name (e.g., tests forasset_pricing.pyshould be found inquantecon/tests/test_asset_pricing.py). - Test files should contain test functions prefixed with
test_.
Test Fundamentals
The basic premise of testing in Python is to write functions that make assertions. An assertion checks a given logical condition. If the condition is met, then the program continues onto the next line. If not, the assertion will trigger an Exception and issue an AssertionError. Here’s a simple example:
def test_equal(a, b):
"""
This tests for equality between two arguments a and b
"""
assert a == b, "Test failed: Arguments are not equal."
If a=2 and b=1 then this test would fail and raise an AssertionError with the message string after the comma.
pytest parses Python files in the QuantEcon repository and collects all tests (i.e., all functions that satisfy the test naming convention discussed below). It then runs them one by one and provides you with a report of which passed and which failed.
To run the test suite, you need to type pytest at the command line, or pytest -v for a more verbose report.
Test Function Names
When pytest parses the repository looking for tests, it will search for functions prefixed with test_. Examples:
test_somethingheretest_my_function_returns_correct_value
When naming your test function, remember to use PEP8 convention. Also make sure you clearly indicate what part of a module (be it a function or class etc.) that your test is testing.
Assertion Methods
While it’s fine to construct your own logic and messaging using assert statements as above, note that there are also many helpful pre-existing assertion methods available in other packages, such as
These packages are used throughout QuantEcon, so it is safe to use functions and methods from them directly using import statements such as:
from numpy.testing import assert_allclose
This particular function is useful in testing if an array matches a known solution, allowing for a degree of tolerance through the rtol= relative tolerance or atol= absolute tolerance keyword arguments.
To learn more, you can browse some of the files in quantecon/tests/ and learn from these examples.
Example: A Basic Test
Let’s look at an example, concerning a basic test for the mc_compute_stationary function from the mc_tools.py module.
We will use a known matrix and compute its stationary distribution.
\[P := \left( \begin{array}{cc} 0.4 & 0.6 \\ 0.2 & 0.8 \end{array} \right)\]We know that the unique stationary distribution should be (0.25, 0.75)
Now let’s write a test case in the file: tests/test_mc_tools.py:
import numpy as np
from numpy.testing import assert_allclose
from quantecon import mc_compute_stationary
def test_mc_compute_stationary_pmatrix():
"""
Test mc_compute_stationary for a Known Solution of Matrix P
"""
P = np.array([[0.4, 0.6], [0.2, 0.8]])
P_known = np.array([0.25, 0.75])
computed = mc_compute_stationary(P)
assert_allclose(computed, P_known)
Note: We use assert_allclose rather than exact equality because computed results and perfect analytical results are often very close but not quite equal due to floating point precision.
Making Tests More General
You can use pytest’s parametrize decorator to run the same test with multiple inputs:
import pytest
import numpy as np
from numpy.testing import assert_allclose
from quantecon import mc_compute_stationary
@pytest.mark.parametrize("P,expected", [
(np.array([[0.4, 0.6], [0.2, 0.8]]), np.array([0.25, 0.75])),
(np.array([[1.0, 0.0], [0.0, 1.0]]), np.array([0.5, 0.5])),
])
def test_mc_compute_stationary_pmatrix(P, expected):
"""
Test mc_compute_stationary for known solutions
"""
computed = mc_compute_stationary(P)
assert_allclose(computed, expected)
Running Tests
To run all tests:
pytest
To run tests in a specific file:
pytest quantecon/tests/test_mc_tools.py
To run a specific test:
pytest quantecon/tests/test_mc_tools.py::test_mc_compute_stationary_pmatrix