Doctest 101: Write Tests as You Write Code
Introduction
When it comes to writing tests for your Python code, doctest
offers an easy, readable, and Pythonic way to go about it. In this guide, we'll take a deep dive into how you can use doctest
to write test cases right in your docstrings, making your code self-testing and more robust.
What is Doctest?
doctest
is a Python standard library module that allows you to test your code by running examples embedded in your docstrings. This makes your documentation not just descriptive but also verifiable, ensuring that your code performs as expected.
Getting Started
The basic idea behind doctest
is to write tests as part of your docstrings. Here's a simple example:
def add(a, b):
"""
Adds two numbers together.
>>> add(2, 3)
5
>>> add(-1, 1)
0
"""
return a + b
Running Doctests
Command-Line Interface
You can run doctest
via the command line
python -m doctest your_module.py
Within Your Code
You can also run doctest
programmatically within your code:
import doctest
doctest.testmod()
Writing Effective Tests
Handling Exceptions
You can test exception-raising functions like so:
def divide(a, b):
"""
Divides a by b.
>>> divide(4, 2)
2.0
>>> divide(1, 0)
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
"""
return a / b
Floating Point Numbers
Be careful with floating-point numbers due to rounding errors:
def square_root(x):
"""
Returns the square root of x.
>>> square_root(4)
2.0
>>> square_root(2)
1.4142135623730951 # may vary
"""
return x ** 0.5
Floating Point Numbers: Avoiding Pitfalls
Dealing with floating-point numbers can be a bit tricky because of rounding errors and imprecise representations. However, doctest
provides ways to manage these issues effectively.
Using round()
One approach is to use Python's round()
function to round the results to a specific decimal place:
def square_root(x):
"""
Returns the square root of x, rounded to 2 decimal places.
>>> round(square_root(2), 2)
1.41
"""
return x ** 0.5
String-based Comparison
If you want more control over the format, you can convert the numbers to strings and then compare:
def square_root(x):
"""
Returns the square root of x as a string, rounded to 2 decimal places.
>>> "{:.2f}".format(square_root_str(2))
'1.41'
"""
return x ** 0.5
Using math.isclose()
For more complex scenarios, you can use Python's math.isclose()
method to check if two floating-point numbers are close enough to be considered equal:
import math
def test_square_root():
"""
>>> math.isclose(square_root(2), 1.41421356237, rel_tol=1e-9)
True
"""
pass
This method allows you to specify a relative tolerance level, making it highly customizable.
By adopting these strategies, you can write more robust and accurate tests for functions that return floating-point numbers.
Advanced Features
Verbose Mode
You can run doctest
in verbose mode to see a detailed log:
python -m doctest -v your_module.py
Skipping Tests
To skip a test, you can add a doctest: +SKIP
directive:
>>> print("This will be skipped.") # doctest: +SKIP
Conclusion
doctest
provides a quick and easy way to write tests for your Python code, right within your docstrings. It encourages good documentation practices while ensuring that your code is robust and bug-free.
So go ahead, make your docstrings work for you by making them testable!