Integration with Other Test Frameworks
This module provides seamless integration of other Python test frameworks—specifically the standard library’s **unittest** framework and **doctest**—into the pytest testing ecosystem. Its purpose is to allow users to run tests written in these formats alongside native pytest tests, benefiting from pytest’s rich features such as fixture injection, detailed reporting, and test selection, without modifying existing unittest or doctest test code.
Overview
Integration with other test frameworks addresses the challenge of supporting legacy or alternative test styles transparently within pytest. This module enables pytest to:
Discover and collect unittest-style test cases and test methods as pytest test items.
Execute unittest tests while mapping unittest lifecycle events and result states into pytest’s execution and reporting system.
Discover and run doctests embedded in Python modules or in separate text files.
Provide enhanced reporting for doctest failures with rich output formats and partial failure handling.
Make fixtures available to doctests, enabling better resource management and test setup.
Unittest Integration
Purpose and Problems Solved
The unittest integration allows pytest to recognize classes derived from `unittest.TestCase` and collect their test methods as pytest test items. This enables users to run existing unittest tests without rewriting them and to leverage pytest’s advanced features such as fixtures and plugins on these tests.
How It Works
Test Discovery: During collection, the hook
pytest_pycollect_makeiteminspects objects in modules and classes. If an object is a concrete subclass ofunittest.TestCase(not abstract and marked for testing), it is collected as aUnitTestCasenode.Collection of Test Methods: The
UnitTestCasecollector uses Python’sunittest.TestLoaderto find test method names. Each method is wrapped as aTestCaseFunctionpytest item.Fixture Integration: To integrate unittest setup and teardown methods with pytest’s fixture system,
UnitTestCaseregisters autouse fixtures that wrap:setUpClassandtearDownClassas a class-scoped fixture, managing exceptions and cleanup.setup_method and teardown_method as function-scoped fixtures.
Test Execution: The
TestCaseFunctionitem overrides theruntestmethod to execute the unittest test case method. It handles async tests, integrates with pytest’s tracing and debugging facilities, and manages teardown postponement when debugging.Result Handling: The test result methods (
addError,addFailure,addSkip,addExpectedFailure,addUnexpectedSuccess,addSuccess) map unittest outcomes to pytest exceptions and record exception info for reporting.Special Support for Twisted Trial: The integration includes compatibility with twisted.trial.unittest by adapting error info and registering pytest as a reporter interface when Twisted is detected.
Interaction with Other Parts of the System
Relies on pytest’s fixture manager (e.g.,
_fixturemanager._register_fixture) to manage unittest setup/teardown lifecycle through fixtures.Uses pytest’s hook system (
@hookimpl) to intercept test collection and test run protocol.Interfaces with pytest’s outcomes module to convert unittest results into pytest exceptions (
skip,fail,xfail).Uses monkeypatching (
MonkeyPatch) to adapt behavior in Twisted versions for correct exception info handling.Coordinates with pytest’s reporting phase to attach unittest exception info to test reports.
Code Snippet Illustration
def pytest_pycollect_makeitem(collector, name, obj):
# Detect unittest.TestCase subclasses for collection
if issubclass(obj, unittest.TestCase) and not inspect.isabstract(obj):
return UnitTestCase.from_parent(collector, name=name, obj=obj)
class UnitTestCase(Class):
def collect(self):
# Use unittest's TestLoader to discover test methods
loader = unittest.TestLoader()
for name in loader.getTestCaseNames(self.obj):
yield TestCaseFunction.from_parent(self, name=name)
def _register_unittest_setup_class_fixture(self, cls):
# Register autouse fixture to run setUpClass and tearDownClass
self.session._fixturemanager._register_fixture(
name=f"_unittest_setUpClass_fixture_{cls.__qualname__}",
func=unittest_setup_class_fixture,
scope="class",
autouse=True,
)
Doctest Support
Purpose and Problems Solved
The doctest support enables pytest to automatically discover and run doctests found within Python modules or external text files. Doctests validate code examples embedded in docstrings or documentation, ensuring examples remain accurate and executable. This integration:
Automates collection of doctests from modules and files matching patterns.
Provides pytest-style reporting with improved failure messages and output formats.
Supports fixture injection into doctest namespaces.
Allows configurable doctest option flags and reporting styles.
Enables partial failure continuation and integration with pytest’s skipping and debugging features.
How It Works
Test Discovery: The
pytest_collect_filehook inspects files:.pyfiles with--doctest-modulesenabled are collected asDoctestModule.Text files (e.g.,
.txt,.rst) matching configured glob patterns are collected asDoctestTextfile.
Doctest Parsing and Item Creation:
Uses Python’s
doctestmodule with a customizedDocTestFinderandDocTestRunner.Each discovered doctest is wrapped as a
DoctestItempytest item.
Test Execution:
The
DoctestItemruns the doctest using a specialized runner that collects failures into a list.Supports options to continue after first failure or stop immediately (e.g., with
--pdb).Injects pytest fixtures into the doctest’s global namespace (via the
doctest_namespacefixture).
Reporting:
Enhanced failure reporting formats diffs using user-selected styles (
udiff,cdiff,ndiff).Multiple failures are aggregated, and detailed context with file and line numbers is provided.
Skips tests if all doctest examples have the
+SKIPdirective.
Option Flags and Namespace Fixtures:
Supports doctest option flags through pytest ini settings.
Provides a
doctest_namespacefixture to inject shared variables or mocks into doctests.
Interaction with Other Parts of the System
Integrates with pytest’s collection system (
pytest_collect_file) to register doctest modules and text files.Uses pytest’s fixture system to inject fixtures into doctest globals.
Employs pytest’s configuration and command-line option parsing for doctest control.
Leverages pytest’s reporting infrastructure to render failure messages and tracebacks.
Interacts with pytest’s outcomes to support skip and exit handling.
Code Snippet Illustration
def pytest_collect_file(file_path, parent):
if file_path.suffix == ".py" and parent.config.option.doctestmodules:
return DoctestModule.from_parent(parent, path=file_path)
elif _is_doctest(parent.config, file_path, parent):
return DoctestTextfile.from_parent(parent, path=file_path)
class DoctestItem(Item):
def setup(self):
# Inject fixtures into doctest globals before running
self._request._fillfixtures()
self.dtest.globs.update(self._request.getfixturevalue("doctest_namespace"))
def runtest(self):
failures = []
self.runner.run(self.dtest, out=failures)
if failures:
raise MultipleDoctestFailures(failures)
Summary of Key Concepts and Design Patterns
Collector and Item Hierarchy: Both unittest and doctest integration use pytest’s collector-item model to represent test suites and individual tests.
UnitTestCaseandDoctestModuleact as collectors, whileTestCaseFunctionandDoctestItemrepresent executable tests.Fixture Bridging: The unittest integration wraps unittest setup/teardown lifecycle methods inside pytest fixtures to unify resource management. Doctests gain access to pytest fixtures by injecting them into the doctest global namespace.
Outcome Translation: Unittest result callbacks are translated to pytest exceptions (
skip.Exception,fail.Exception,xfail.Exception) to leverage pytest’s reporting and control flow.Lazy and Conditional Integration: Twisted Trial support is conditionally enabled if the Twisted framework is present, adapting exception handling accordingly.
Enhanced Reporting: Doctest failures produce rich terminal representations with configurable diff formats and contextual source excerpts to improve usability.
Mermaid Sequence Diagram: Unittest Integration Workflow
sequenceDiagram
participant Pytest as Pytest Collection
participant UnitTestClass as unittest.TestCase Subclass
participant UnitTestLoader as unittest.TestLoader
participant TestMethod as TestCaseFunction Item
participant FixtureMgr as Pytest Fixture Manager
participant TestRunner as unittest TestCase Runner
Pytest->>UnitTestClass: Check if subclass of unittest.TestCase
UnitTestClass->>UnitTestLoader: Get test method names
UnitTestLoader-->>Pytest: List of test method names
Pytest->>TestMethod: Create pytest test items for methods
Pytest->>FixtureMgr: Register class and method setup/teardown fixtures
TestMethod->>TestRunner: On runtest(), execute test method
TestRunner-->>TestMethod: Report test result (success, failure, skip)
TestMethod->>Pytest: Raise pytest exceptions on failure/skip for reporting
Mermaid Flowchart: Doctest Collection and Execution
flowchart TD
A[Start Collection] --> B{Is file a .py module?}
B -- Yes & --doctest-modules --> C[Collect DoctestModule]
B -- No --> D{Is file a doctest text file?}
D -- Yes --> E[Collect DoctestTextfile]
D -- No --> F[Skip file]
C --> G[Find doctests in module]
E --> H[Parse doctests from text]
G --> I[Create DoctestItem for each test]
H --> I
I --> J[Run DoctestItem]
J --> K{Failures?}
K -- Yes --> L[Report failures with diff]
K -- No --> M[Test passed]
This documentation explains the integration of unittest and doctest frameworks within pytest, focusing on how tests from these frameworks are discovered, executed, and reported, thereby enabling users to leverage pytest’s capabilities on a wide variety of test formats.