Testing

1. Introduction

There are a number of different types of testing involved in developing software, some key ones are: functionality testing, performance testing and usability testing. Functionality testing aims to assure that functions behave as expected. Performace testing helps to explore the responsiveness, stability, scalability, reliability and resource usage of software. Usability testing aims to ensure user interfaces are easy to use and understand. In ABM3 there is an exercise in performace testing to optimise a function. This section focuses on functionality testing and some different ways to do this in Python that goes beyond the use of print statements which are also fundamental for a lot of testing and issue diagnosis. As you should know by now, testing is key to developing code and creating reproducible results.

2. Doctest

The Python Standard Library comes with doctest a module that can be used to search for text that looks like interactive Python sessions, and then executes those sessions to verify that they work as shown. Such text is sometimes placed in a docstring as per the example in 'calculator_doctest.py'.

Save calculator_doctest.py, inspect the code and run it. It should run without reporting anything. Try altering "0.3" to "0.30000000000000004" and run the file again. The output should be along the following lines:

File "calculator_doctest.py", line 25, in __main__.add
Failed example:
    add(0.1, 0.1, 0.1)
Expected:
    0.30000000000000004
Got:
    0.3
**********************************************************************
1 items had failures:
   1 of   2 in __main__.add
***Test Failed*** 1 failures.

3. Unit Tests

Unit Testing is where individual units of source code - together with associated control data, usage procedures, and operating procedures - are tested to determine whether they are fit for use. It aims to reduce software development risks, time, and costs.

In test driven development, the tests are written before the functional code. When the code passes the tests, if the tests have good enough coverage, then the functionality is successfully implemented.

It can be good to separate testing code from functional code so that testing code does not have to be distributed with functional code. In some software development the overall size of the testing code is huge compared to the functional code, so separating this out allows for distributions to be smaller. Organising this is involves packaging code to have a separate test package. Usually, the structure of the test package is the same as the main code package structure making the tests for specific modules, classes and functions to be easily found and updated.

The Python Standard Library comes with unittest a module providing tools for constructing and running tests.

Consider the following example. The functional code is in 'calculator.py', the module contains a single function 'add' which adds up numbers provided as Integer or Float Numeric Types using decimal to avoid some floating-point rounding issues.

Save calculator.py and test.py in the same directory, inspect the code and run 'test.py'. For more details about developing unit tests in Python, see: unittest.