Session 12: Testing Python Code and Code Coverage
Date: 30-05-2026 4:00 pm to 6:00 pm
Agenda
- Review of previous sessions
- Answers to queries
- Code testing using
pytest - Code coverage report using
pytest-cov
Code Testing and Coverage
Testing code is an integral part of software development and must not be an afterthought. While print() statements and debugging are constantly done during application development, that is not to be treated as testing. Testing must be:
- Systematic and exhaustive. Every branch of a a logical statement and every possible input data must be tested.
- Cover all lines of code and not just the most obvious cases.
Testing requires an ability to detatch oneself from the code they are developing and look at it critically. A thorough understanding o fthe problem domain must be known in order to craft all possible cases for testing. One must be able to write tests for the code that may have been written by others. Thus, one must be able to write exhaustive tests for the code written by themselves.
There are several tools that simplify the process of writinf tests and reorting the code covered by the tests so as to identify the code that has not been covered by the tests. This helps one to write additional tests to ensure coverage of entire code. Merely ensuring 100% code coverage does not assure thoroughnes of tests. With the domain knowledge, one must devise edge cases and ensure that the code behaves as expected under the most extreme cases.
Code testing using pytest
We took a brief look at pytest in Session 4, but we will take a closer look now.
pytest is a Python framework for functional testing of Python applications and libraries. It has the following features:
- Auto-discovery of test modules and functions. Modules with names startng with
test_*.pyor_test.pyare automatically considered as containing tests and functions within these modules with names starting withtest_are assumed to be test functions. To test libraries, one can create a separate directory containing test modules and pointpytestto this directory. - Testing is carried out using
assertstatements within the test functions. - Extendible with plugins. The plugin
pytest-covprovides code coverage functionality topytestusingcoveragemodule.
Installing pytest and pytest-cov
Install pytest and pytest-covwith the following commands from the command line and test that it is installed correctly:
Writing tests
Tests are written in a Python module with its name starting with test_*.py. Functions within a test module are considered to be test functions if their name starts with test_. The test functions can be split between more than one test module if necessary.
We can take the code in the if __name__ == "__main": and convert them into tests, as shown below:
Notes
isclose()is imported frommathin order to determine equality of floating point numbers which are sufficiently close.rect_footingis imported as a module so that the classRectFootingis available to the testing module..- Any functions that may be required by the testing functions can be defined, but their names must not begin with
test_, as otherwise they too will be considered as test functions. - It is best if each test function tests one aspect of the code. For example,
test_area()tests whether the application calculates the required area correctly. - A single function can test multiple aspects for the same parameter. For example,
test_sides()tests for different aspect ratios, equal to1, freaterthan1and less than1. However, edge cases have not been included, for example, aspect ratio of0should have been tested to see how the application behaves in that situation. An aspect ratio that is a negative number is also not tested. - The
assertstatement returnTrueif an assertion succeeds andFalseif it fails. A test function is considered to have passed if all assertions in it pass. - To test if a calculation is correct, one technique is to calculate it from first principles within the module and compare it with the result from our code. However, care must be taken to ensure that the manual calculations are correct.
isclose(x, y)checks if the valuesxandyare sufficiently close to each other. See the documentation formath.isclose()to learn how exactly the comparison is made.
Running tests
Once the tests are written, we can run the tests as follows:
The output must look similar to the following:
================================================= test session starts =================================================
platform win32 -- Python 3.14.3, pytest-9.0.3, pluggy-1.6.0
rootdir: E:\Satish\Python\sci
configfile: pyproject.toml
plugins: cov-7.1.0
collected 2 items
test_rect_footing.py .. [100%]
================================================== 2 passed in 0.22s ==================================================
If a test fails, detailed intermediate values will be displayed to help identify the failing test.
Code coverage report using pytest-cov
When testing, it is important to ensure that all lines of code are tested by the test functions in the test modules. Trying to establish this manually can be difficult and time consuming. That is where the the Coverage.py tool comes in handy. Even better, [pytest-cov] package is a pytest plugin that integrates Coverage.py into pytest instead of making it another additional task.
pytest-cov generates code coverage reports in HTML format and shows which lise of code have not been tested by the test modules. Studying this report helps in writing additional tests so that eventually, all lines of code are tested.
Code coverage report can be generated as follows:
Note: cov_html is the name of the directory where rthe HTML report is saved. This directory is created if it does not already exist, and if it exists, its contents will be replaced by the new report.
The output must look similar to the following:
================================================= test session starts =================================================
platform win32 -- Python 3.14.3, pytest-9.0.3, pluggy-1.6.0
rootdir: E:\Satish\Python\sci
configfile: pyproject.toml
plugins: cov-7.1.0
collected 2 items
test_rect_footing.py .. [100%]
=================================================== tests coverage ====================================================
___________________________________ coverage: platform win32, python 3.14.3-final-0 ___________________________________
Coverage HTML written to dir cov_html
================================================== 2 passed in 0.51s ==================================================
Note
- The tests are run as usual.
- The coverage report is generated after the tests and the name of the directory wherethe report is saved is displayed.
- The
index.htlfile in the report directory is the entry point to the report. Opening this file in a web browser shows the report, which must look similar to the following:

Note
- It shows the percentage of the code that is covered by the tests, 48% in this case. It also shows the statistics in terms of files, functions and classes, by clicking on the respective tabs at the top of the report.
- Clicking on the name of a file, for example,
test_rect_footing.pyin this case shown the file with the lines not covered by the tests with a red background.

Remaining Tests
| test_rect_footing.py | |
|---|---|
Remaining Tasks
Handling invalid input data
One important but frequently neglected aspect of application development is to foresee all types of expected input and to raise exceptions to handle invalid data.
For example, the method def lx_ly(self, sbc: float, aspect_ratio: float, m: float = 0) -> tuple[float, float] will fail under the following input values:
- If
sbcis a negative number, the calculation for area will result in a negative number. - If
aspect_ratiois less than or equal to0, the calculations will fail.
A well written application will validate the input and raise appropriate exceptions when invalid data in encountered.
Source code comments
Comments are reminders for the programmer for the future when you revisit the source code after a long gap of time. It usually happens that as long as the application is giving results, we tend to ignore the source code. After a long gap of time, we tend to forgetthe intricacies in the code. When a bug crops up, and you revisitthe code, it is useful to have comments to remind oneself the claer intent of the code.
Comments are useful to other people who read your code. Since the code is new to them, the comments will clarify the intent of the programmer who wrotethe code.
Source code documentation
Python PEP 8 is style guide for Python code. It describes the coding conventions for the Python code comprising the standard library in the main Python distribution.
PEP 257 describes Docstring conventions. Docstrings are used by the Python REPL to generate help withn the REPL. Several linters available within code editors can use these Docstrings to generate autocomplete prompts.
Google Doc Style is the convention for writing Docstrings in the source code that can be extracted by programs such as Material for MkDocs, Zensical or Sphinx to generate documentation for developers who wish to understand the code and modify the source code.
An alternative to the Google Doc Style is the NumPy Doc Style followed by NumPy.
Sourc code documentation generators extract he Docstrings and generate documentation in HTML or other formats.