How#

How to write an assert statement?#

An assert statement is followed by 2 values: a boolean and an optional string that gets returned if the boolean is False.

Tip

assert boolean, string

For example, given a function that adds one to a variable:

def add_one(a):
    """
    Returns a + 1
    """
    return a + 1

We can assert that we have the expected behaviour:

assert add_one(5) == 6, "The function gave the wrong answer."

Note that if we change the function to include an error for example here adding 2 and not 1, and run the same assert we get an error as well as the specific string,

def add_one(a):
    """
    Returns a + 1
    """
    return a + 2


assert add_one(5) == 6, "The function gave the wrong answer."
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
Cell In[3], line 8
      2     """
      3     Returns a + 1
      4     """
      5     return a + 2
----> 8 assert add_one(5) == 6, "The function gave the wrong answer."

AssertionError: The function gave the wrong answer.

How to write assert statements for code that acts randomly?#

When making an assertion about code that behaves in a random manner, we use seeding as described in Reproduce random events.

For example:

import random


def roll_a_dice():
    """
    Pick a random integer between 1 and 6 (inclusive)
    """
    return random.choice(range(1, 7))

To test this we can include a number of seeded assertions:

random.seed(0)
assert roll_a_dice() == 4, "The 0 seed did not give the expected result"
random.seed(1)
assert roll_a_dice() == 2, "The 1 seed did not give the expected result"
random.seed(2)
assert roll_a_dice() == 1, "The 2 seed did not give the expected result"
random.seed(3)
assert roll_a_dice() == 2, "The 3 seed did not give the expected result"

We can also check behaviour over a number of repetitions:

random.seed(0)
samples = [roll_a_dice() for repetition in range(1000)]
all_values = {1, 2, 3, 4, 5, 6}
assert set(samples) == all_values, "Not all values have been obtained over 1000 repetitions"

We can also confirm that the count of a given value is in an expected range:

assert [samples.count(k) for k in range(1, 7)] == [193, 150, 166, 170, 152, 169], "The count of the values is not giving the expected count"

Tip

The last assertion used the count method on a list that counts a given number of items in a list.

How to write a test file?#

To write tests assertion statement can be put in to a file separate to the code in functions.

For example, if the dice.py file contained:

import random


def roll_a_dice():
    """
    Pick a random integer between 1 and 6 (inclusive)
    """
    return random.choice(range(1, 7))

Then a separate test_dice.py file with the following would be written:

import dice


def test_roll_a_dice_with_specific_values():
    """
    Check the roll a dice function gives specific numbers for a number of seeds.
    """
    random.seed(0)
    assert dice.roll_a_dice() == 4, "The 0 seed did not give the expected result"
    random.seed(1)
    assert dice.roll_a_dice() == 2, "The 1 seed did not give the expected result"
    random.seed(2)
    assert dice.roll_a_dice() == 1, "The 2 seed did not give the expected result"
    random.seed(3)
    assert dice.roll_a_dice() == 2, "The 3 seed did not give the expected result"


def test_roll_a_dice_for_a_large_sample():
    """
    Collect a sample of 1000 rolls and confirm that we have expected results.
    """
    random.seed(0)
    samples = [dice.roll_a_dice() for repetition in range(1000)]
    all_values = {1, 2, 3, 4, 5, 6}
    assert set(samples) == all_values, "Not all values have been obtained over 1000 repetitions"
    expected_counts = [193, 150, 166, 170, 152, 169]
    assert [samples.count(k) for k in range(1, 7)] == expected_counts, "The count of the values is not giving the expected count"

test_roll_a_dice_with_specific_values()
test_roll_a_dice_for_a_large_sample()

To run the tests we would then type the following at the command line:

$ python test_dice.py

How to format doc tests?#

When writing code in documentation if we write it using a specific format then python can be used to confirm that the code and its output is correct.

Tip

>>> <python_code>
<expected_output
>>> <python_code_over_multiples_lines>
... <python_code_over_multiple_lines>
<expected_output>
  • >>> is marks the start of a python statement.

  • ... is used if the statement is multiple lines.

  • The expected output is written after the python statements.

For example if we were documenting the following code written in a dice.py file:

import random


def roll_a_dice():
    """
    Pick a random integer between 1 and 6 (inclusive)
    """
    return random.choice(range(1, 7))

We would write:

>>> import dice
>>> random.seed(0)
>>> dice.roll_a_dice()
4

How to run doctests?#

Given a with doctests to run them we type the following at the command line:

Tip

$ python -m doctest <file>

For example:

python -m doctest README.md