Log Book With Computer Bug from Mark II computer at Harvard University (1947). Smithsonian Collection.

Tests using the unittest.mock framework have a lot of test_ prefixes: methods are prefixed with test_, classes are prefixed with Test_ and they are stored in files with filenames prefixed with test_.

Finding the Bug

The unit test below shows a simple test of a TemperaturePlotSource object created in Kelvin mode and tested with values starting at 20 degrees Celsius which would equate to 293.15K.

# an excerpt from Test_TemperaturePlotSource class
    SENSOR_DATA = (20, 21.3, 22.0, 0.0, -40, 85)

    def test_kelvin(self):
        """Create the source in Kelvin mode and test with some values."""
        # Emulate the clue's temperature sensor by
        # returning a temperature from a small tuple
        # of test data
        mocked_clue = Mock()
        expected_data = (293.15, 294.45, 295.15,
                         273.15, 233.15, 358.15)
        type(mocked_clue).temperature = PropertyMock(side_effect=self.SENSOR_DATA)

        source = TemperaturePlotSource(mocked_clue,

        for expected_value in expected_data:
            data =
            # self.assertEqual(data,
            #                 expected_value,
            #                 msg="An inappropriate check for floating-point")
                                   msg="Checking converted temperature is correct")

There are three tests in a Test_TemperaturePlotSource class. Python on a desktop computer (or Raspberry Pi) can execute these. One run is shown below.

$ python tests/
test_celsius (__main__.Test_TemperaturePlotSource)
Create the source in Celsius mode and test with some values. ... ok
test_fahrenheit (__main__.Test_TemperaturePlotSource)
Create the source in Fahrenheit mode and test with some values. ... ok
test_kelvin (__main__.Test_TemperaturePlotSource)
Create the source in Kelvin mode and test with some values. ... FAIL

FAIL: test_kelvin (__main__.Test_TemperaturePlotSource)
Create the source in Kelvin mode and test with some values.
Traceback (most recent call last):
  File "tests/", line 101, in test_kelvin
    msg="Checking converted temperature is correct")
AssertionError: 20.0 != 293.15 within 7 places : Checking converted temperature is correct

Ran 3 tests in 0.002s

FAILED (failures=1)

The Celsius and Fahrenheit tests are fine (ok output) but the Kelvin one is failing. The failure is reported with the value from the first comparison. For some reason, the value 20.0 is being returned when we expect 293.15, no conversion is taking place and the value has been left in Celsius for some reason.

Dual scale Jolni thermometer. Photograph by Kevin Walters.

A previous code inspection showed an anomaly with the use of lower() vs lower. CircuitPython's REPL allows us to explore this interactively on the command line, using the CLUE board.

The absence of the round brackets on the end of the lower method call introduces a critical flaw into the code. CircuitPython is somehow comparing a method call with a string. This "chalk and cheese" comparison is very unfortunate here!

Adafruit CircuitPython 5.0.0 on 2020-03-02; Adafruit CLUE nRF52840 Express with nRF52840
>>> mode="Kelvin"
>>> mode[0].lower == "k"
>>> mode[0].lower == "K"
>>> mode[0]
>>> mode[0].lower
>>> mode[0].lower() == "k"

Python is often referred to as strongly and dynamically-typed but an equality test using == will compare anything against anything - if they are not equivalent in some way then it will evaluate as False.

For comparison, this is the approximate equivalent in C++ using a different member function (C++ terminology for a method) called front.

std::string Family("Coronaviridae");

if (Family.front() == 'N') {
  std::cout << "Starts with N" << std::endl;
} else if (Family.front == 'C') {
  std::cout << "Starts with C" << std::endl;

The compilation aborts with an error because the types cannot be legitimately compared using the C++ type system. The relative strength of type systems is a subjective topic and can vary in practical terms based on how a language is used.

$ g++ -std=c++11 -o type-demonstration type-demonstration.cpp     type-demonstration.cpp: In function ‘int main(int, char**)’:
type-demonstration.cpp:15:30: error: invalid operands of types ‘<unresolved overloaded function type>’ and ‘char’ to binary ‘operator==’
   } else if (Family.front == 'C') {

A language which checks types at compile-time is referred to as statically-typed.

Sometimes other software tools can help find likely bugs. In this case pylint has some ability to find dubious comparisons but it needs to be able to determine the type accurately and this prevents it from finding this particular bug. The simpler case is shown below where pylint presents a warning (W in W0143) about a possible mistake by the programmer.

if STRING[0].lower == "h":  # not what programmer intended!
    print("Found an h or an H")  # won't even be executed
$ pylint
************* Module check_pylint_comparison_with_callable W0143: Comparing against a callable, did you omit the parenthesis? (comparison-with-callable)

Fixing the Bug

Adding the missing () is all that's required but the test still fails. The error message has changed.

AssertionError: -253.14999999999998 != 293.15 within 7 places : Checking converted temperature is correct

This guide was first published on Apr 01, 2020. It was last updated on Feb 29, 2024.

This page (Bug 1) was last updated on Mar 26, 2020.

Text editor powered by tinymce.