Python tutorials > Testing > Doctests > How to run doctests?

How to run doctests?

Understanding and Running Doctests in Python

Doctests are a simple way to test your Python code by embedding test cases directly within your docstrings. This tutorial explains how to run doctests and interpret their results.

Basic Doctest Structure

A doctest is a snippet of Python code embedded in a docstring. The code is preceded by >>>, and the expected output immediately follows on the next line(s). The add function above includes two doctests.

def add(a, b):
    """Return the sum of a and b.

    >>> add(2, 3)
    5
    >>> add(-1, 1)
    0
    """
    return a + b

Running Doctests from the Command Line

The most straightforward way to run doctests is from the command line. Replace your_module.py with the name of the Python file containing the doctests. The -m doctest option tells Python to run the doctest module.

python -m doctest your_module.py

Running Doctests within a Python Script

You can also run doctests within a Python script. The doctest.testmod() function automatically finds and executes all doctests in the specified module. Make sure to replace your_module with the actual name of your module.

import doctest
import your_module  # Replace with your module name

if __name__ == "__main__":
    doctest.testmod(your_module)

Interpreting Doctest Results

When you run doctests, the output will indicate whether the tests passed or failed. If all tests pass, you'll see a message similar to:

Trying
    add(2, 3)
Expecting:
    5
ok
Trying
    add(-1, 1)
Expecting:
    0
ok
1 items had no tests:
    __main__
1 items passed all tests:
   2 tests in __main__.add
2 tests in 2 items.
2 passed and 0 failed.
Test passed.

If a test fails, you'll see a detailed error message showing the expected and actual output.

Concepts Behind the Snippet

Doctests leverage the Python interpreter to execute code snippets found in docstrings. The doctest module parses these snippets, runs them, and compares the actual output with the expected output specified in the docstring. This provides a simple, integrated way to verify that your code behaves as documented.

Real-Life Use Case

Imagine you're building a complex mathematical library. You can use doctests to verify that each function produces the correct results for various inputs. This allows you to quickly identify and fix bugs as you develop the library.

For instance, consider a function for calculating the factorial of a number. You can include doctests that cover cases like factorial(0), factorial(1), and factorial(5) to ensure the function behaves correctly for edge cases and typical inputs.

Best Practices

  • Keep tests concise: Doctests should be short and focused on verifying specific behavior.
  • Include edge cases: Test your code with boundary conditions and invalid inputs.
  • Write clear expected output: Ensure the expected output is unambiguous and easy to understand.
  • Don't rely on external state: Doctests should be self-contained and not depend on global variables or external resources.

Interview Tip

When discussing testing in Python interviews, mentioning doctests showcases your awareness of built-in testing tools. Highlight their simplicity and ease of integration into your workflow. Be prepared to explain the advantages and disadvantages of using doctests compared to more advanced testing frameworks like pytest or unittest.

When to Use Them

Doctests are ideal for:

  • Simple functions and modules.
  • Providing executable examples in documentation.
  • Quickly verifying basic functionality.

They are less suitable for complex testing scenarios that require setup, teardown, or mocking.

Memory Footprint

Doctests generally have a minimal memory footprint. They only require the resources needed to execute the code snippets and compare the output. The impact on memory usage is usually negligible, especially for small and focused tests.

Alternatives

Alternatives to doctests include:

  • unittest: A more structured testing framework based on classes and methods.
  • pytest: A popular and flexible testing framework with features like auto-discovery and fixtures.
  • nose2: Another testing framework with similar features to pytest.

Pros

  • Simple and easy to use: No external dependencies or complex setup required.
  • Integrated documentation and testing: Tests are embedded directly in the docstrings.
  • Executable examples: Docstrings serve as both documentation and test cases.

Cons

  • Limited functionality: Less powerful than dedicated testing frameworks.
  • Difficult to manage complex tests: Can become unwieldy for large projects.
  • Docstrings can become cluttered: Tests can make docstrings harder to read.
  • Output matching is strict: Small variations in output can cause tests to fail.

FAQ

  • How do I handle floating-point numbers in doctests?

    Floating-point numbers can be tricky due to their inherent imprecision. Use the round() function to compare results to a certain number of decimal places. Alternatively, you can use an approximate comparison using a tolerance value.

  • Can I ignore certain lines in the output during doctest comparison?

    Yes, you can use the doctest.ELLIPSIS option to ignore parts of the output. This is useful when the output contains dynamic content like memory addresses or timestamps.

    For example:

    >>> import datetime
    >>> print(datetime.datetime.now())
    2023-10-27 10:00:00.123456
    >>> # doctest: +ELLIPSIS
    ...
  • How do I test functions that raise exceptions?

    You can use the doctest.IGNORE_EXCEPTION_DETAIL option to match the exception type without matching the exception message. Also, using assertRaises context manager with unittest in a doctest can be useful, although it can make them less readable.

    Example:

    >>> def divide(a, b):
    ...     if b == 0:
    ...         raise ValueError("Cannot divide by zero")
    ...     return a / b
    >>> divide(10, 0)
    Traceback (most recent call last):
        ...
    ValueError: Cannot divide by zero