From 5b4d59f3876e633517c9d01932caadf69d3990b0 Mon Sep 17 00:00:00 2001 From: Liam Pattinson Date: Tue, 17 Feb 2026 19:23:56 +0000 Subject: [PATCH] Tweaks to parametrization --- episodes/06-floating-point-data.Rmd | 9 ++++++--- episodes/08-parametrization.Rmd | 19 ++++++++++--------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/episodes/06-floating-point-data.Rmd b/episodes/06-floating-point-data.Rmd index 3e56e18..d1fd5aa 100644 --- a/episodes/06-floating-point-data.Rmd +++ b/episodes/06-floating-point-data.Rmd @@ -32,7 +32,6 @@ places. This can be affected by: - Choice of algorithm. - Precise order of operations. -- Order in which parallel processes finish. - Inherent randomness in the calculation. We could therefore test our code using `assert result == 2.34958124890e-31`, @@ -54,8 +53,8 @@ it is best to use a _relative_ tolerance: from math import fabs def test_float_rtol(): - actual = my_function() expected = 7.31926e12 # Reference solution + actual = my_function() rtol = 1e-3 # Use fabs to ensure a positive result! assert fabs((actual - expected) / expected) < rtol @@ -69,8 +68,8 @@ tolerance: from math import fabs def test_float_atol(): - actual = my_function() expected = 0.0 # Reference solution + actual = my_function() atol = 1e-5 # Use fabs to ensure a positive result! assert fabs(actual - expected) < atol @@ -90,6 +89,10 @@ inefficiently!). import random def estimate_pi(iterations): + """ + Estimate pi by counting the number of random points + inside a quarter circle of radius 1 + """ num_inside = 0 for _ in range(iterations): x = random.random() diff --git a/episodes/08-parametrization.Rmd b/episodes/08-parametrization.Rmd index 7211577..1cd108b 100644 --- a/episodes/08-parametrization.Rmd +++ b/episodes/08-parametrization.Rmd @@ -98,9 +98,9 @@ This test is quite long and repetitive. We can use parametrization to make it mo import pytest @pytest.mark.parametrize( - ("p1x, p1y, p2x, p2y, p3x, p3y, expected"), + "p1x, p1y, p2x, p2y, p3x, p3y, expected", [ - pytest.param(0, 0, 2, 0, 1, 1.7320, 1.7320, id="Equilateral triangle"), + pytest.param(0, 0, 2, 0, 1, 1.732, 1.732, id="Equilateral triangle"), pytest.param(0, 0, 3, 0, 0, 4, 6, id="Right-angled triangle"), pytest.param(0, 0, 4, 0, 2, 8, 16, id="Isosceles triangle"), pytest.param(0, 0, 3, 0, 1, 4, 6, id="Scalene triangle"), @@ -119,13 +119,14 @@ Let's have a look at how this works. Similar to how fixtures are defined, the `@pytest.mark.parametrize` line is a decorator, letting pytest know that this is a parametrized test. -- The first argument is a tuple, a list of the names of the parameters you want to use in your test. For example -`("p1x, p2y, p2x, p2y, p3x, p3y, expected")` means that we will use the parameters `p1x`, `p1y`, `p2x`, `p2y`, `p3x`, `p3y` and `expected` in our test. +- The first argument is a string listing the names of the parameters you want to use in your test. For example +`"p1x, p2y, p2x, p2y, p3x, p3y, expected"` means that we will use the parameters `p1x`, `p1y`, `p2x`, `p2y`, `p3x`, `p3y` and `expected` in our test. -- The second argument is a list of `pytest.param` objects. Each `pytest.param` object is a tuple of the values you want to test, with an optional `id` argument to give a name to the test. -For example, `pytest.param(0, 0, 2, 0, 1, 1.7320, 6, id="Equilateral triangle")` means that we will test the function with the parameters `0, 0, 2, 0, 1, 1.7320, 6` and give it the name "Equilateral triangle". +- The second argument is a list of `pytest.param` objects. Each `pytest.param` object contains the values you want to test, with an optional `id` argument to give a name to the test. -(note that if the test fails you will see the id in the output, so it's useful to give them meaningful names to help you understand what went wrong.) +For example, `pytest.param(0, 0, 2, 0, 1, 1.732, 1.732, id="Equilateral triangle")` means that we will test the function with the parameters `0, 0, 2, 0, 1, 1.732, 1.732` and give it the name "Equilateral triangle". + +Note that if the test fails you will see the id in the output, so it's useful to give them meaningful names to help you understand what went wrong. - The test function will be run once for each set of parameters in the list. @@ -135,7 +136,7 @@ This is a much more concise way to write tests for functions that need to be tes ::::::::::::::::::::::::::::::::::::: challenge -## Challenge - Practice with Parametrization +## Practice with Parametrization Add the following function to `advanced/advanced_calculator.py` and write a parametrized test for it in `tests/test_advanced_calculator.py` that tests the function with a range of different inputs using parametrization. @@ -158,7 +159,7 @@ def is_prime(n: int) -> bool: import pytest @pytest.mark.parametrize( - ("n, expected"), + "n, expected", [ pytest.param(0, False, id="0 is not prime"), pytest.param(1, False, id="1 is not prime"),