Testing

django-ca uses pytest for running the test suite:

$ pytest -v

This will generate a code coverage report in docs/build/html/.

Test coverage

The test suite must ensure 100% test coverage. Completely excluding code from test coverage is only allowed when absolutely necessary.

Conditional pragmas

In addition to the standard # pragma: no cover and # pragma: no branch, the test suite adds pragmas to exclude code based on the Python version or library versions. For example:

if sys.version_info >= (3, 8):  # pragma: only py>=3.8
   from typing import Literal
else:  # pragma: only py<3.8
   from typing_extensions import Literal

If you have branches that are only relevant for some versions, there’s also pragmas for that:

if sys.version_info >= (3, 8):  # pragma: py>=3.8 branch
   print("Do something that's only useful in Python 3.8 or newer.")
if django.VERSION[:2] >= (3, 2):  # pragma: django>=3.2 branch
   print("Do something that's only useful in Django 3.2 or newer.")

You can use all operators (<, <=, ==, !=, >, >=), and we add pragma for the versions of Python, Django, cryptography.

Please check ca/django_ca/tests/base/pragmas.py for a tested file that includes all supported pragmas. Correctly using the pragmas is mandatory, as they are also used for finding outdated code when older versions are deprecated.

pytest

Fixtures

Pytest fixtures used throughout the code base.

django_ca.tests.base.fixtures.any_cert(request: SubRequest) Certificate[source]

Parametrized fixture for absolutely any certificate name.

django_ca.tests.base.fixtures.ca(request: SubRequest) CertificateAuthority[source]

Parametrized fixture for all certificate authorities known to the test suite.

django_ca.tests.base.fixtures.ca_name(request: SubRequest) str[source]

Fixture for a name suitable for a CA.

django_ca.tests.base.fixtures.certificate_policies(request: SubRequest, certificate_policies_value: CertificatePolicies) Extension[CertificatePolicies][source]

Parametrized fixture yielding different x509.Extension[x509.CertificatePolicies] objects.

django_ca.tests.base.fixtures.certificate_policies_value(request: SubRequest) CertificatePolicies[source]

Parametrized fixture with different CertificatePolicies objects.

django_ca.tests.base.fixtures.clear_cache() Iterator[None][source]

Fixture to clear the cache after the test.

django_ca.tests.base.fixtures.db_backend() DBBackend[source]

Fixture providing a DB backend.

django_ca.tests.base.fixtures.ed_ca(request: SubRequest) CertificateAuthority[source]

Parametrized fixture for CAs with an Edwards-curve algorithm (ed448, ed25519).

django_ca.tests.base.fixtures.hostname(ca_name: str) str[source]

Fixture for a hostname.

The value is unique for each test, and it includes the CA name, which includes the test name.

django_ca.tests.base.fixtures.hsm_backend(request: SubRequest) Iterator[HSMBackend][source]

Fixture providing a HSMBackend with the current token and (randomized) passwords.

django_ca.tests.base.fixtures.hsm_ocsp_backend(request: SubRequest) Iterator[HSMOCSPBackend][source]

Fixture providing a HSMBackend with the current token and (randomized) passwords.

django_ca.tests.base.fixtures.interesting_cert(request: SubRequest) Certificate[source]

Parametrized fixture for “interesting” certificates.

A function using this fixture will be called once for each certificate with unusual extensions.

django_ca.tests.base.fixtures.key_backend(request: SubRequest) StoragesBackend[source]

Return a StoragesBackend for creating a new CA.

django_ca.tests.base.fixtures.precertificate_signed_certificate_timestamps_pub(request: SubRequest) Certificate[source]

Parametrized fixture for certificates that have a PrecertSignedCertificateTimestamps extension.

django_ca.tests.base.fixtures.rfc4514_subject(subject: Name) str[source]

Fixture for an RFC 4514 formatted name to use for a subject.

The common name is based on hostname() and identical to subject().

django_ca.tests.base.fixtures.root_attribute_crl(root: CertificateAuthority) CertificateRevocationList[source]

Fixture for the attribute CRL object for the Root CA.

django_ca.tests.base.fixtures.root_ca_crl(root: CertificateAuthority) CertificateRevocationList[source]

Fixture for the user CRL object for the Root CA.

django_ca.tests.base.fixtures.root_crl(root: CertificateAuthority) CertificateRevocationList[source]

Fixture for the global CRL object for the Root CA.

django_ca.tests.base.fixtures.root_user_crl(root: CertificateAuthority) CertificateRevocationList[source]

Fixture for the user CRL object for the Root CA.

django_ca.tests.base.fixtures.secondary_backend(request: SubRequest) StoragesBackend[source]

Return a StoragesBackend for the secondary key backend.

django_ca.tests.base.fixtures.signed_certificate_timestamp_pub(request: SubRequest) Certificate[source]

Parametrized fixture for certificates that have any SCT extension.

django_ca.tests.base.fixtures.signed_certificate_timestamps_pub(request: SubRequest) Certificate[source]

Parametrized fixture for certificates that have a SignedCertificateTimestamps extension.

Note

There are no certificates with this extension right now, so this fixture is in fact never run.

django_ca.tests.base.fixtures.softhsm_setup(tmp_path: Path) Iterator[Path][source]

Fixture to set up a unique SoftHSM2 configuration.

django_ca.tests.base.fixtures.softhsm_token(request: SubRequest, settings: SettingsWrapper) str[source]

Get a unique token for the current test.

django_ca.tests.base.fixtures.subject(hostname: str) Name[source]

Fixture for a Name to use for a subject.

The common name is based on hostname() and identical to rfc4514_subject().

django_ca.tests.base.fixtures.tmpcadir(tmp_path: Path, settings: SettingsWrapper) Iterator[Path][source]

Fixture to create a temporary directory for storing files using the StoragesBackend.

django_ca.tests.base.fixtures.usable_ca(request: SubRequest) CertificateAuthority[source]

Parametrized fixture for every usable CA (with usable private key).

django_ca.tests.base.fixtures.usable_ca_name(request: SubRequest) CertificateAuthority[source]

Parametrized fixture for the name of every usable CA.

django_ca.tests.base.fixtures.usable_ca_name_by_type(request: SubRequest) CertificateAuthority[source]

Parametrized fixture for the name of a CA of every type ("dsa", "rsa", …).

django_ca.tests.base.fixtures.usable_cas(request: SubRequest) list[CertificateAuthority][source]

Fixture for all usable CAs as a list.

django_ca.tests.base.fixtures.usable_cert(request: SubRequest) Certificate[source]

Parametrized fixture for every {ca}-cert certificate.

The name of the certificate can be retrieved from the non-standard test_name property of the certificate.

django_ca.tests.base.fixtures.usable_hsm_ca(request: SubRequest, ca_name: str, subject: Name, hsm_backend: HSMBackend) CertificateAuthority[source]

Parametrized fixture yielding a certificate authority for every key type.

django_ca.tests.base.fixtures.x448_private_key() X448PrivateKey[source]

Session fixture for an x448 private key.

Generated fixtures

{name}_pub - Certificate

Certificate loaded from test fixture data.

Available for every CA generated in the test fixtures and every certificate. Examples: root_pub, root_cert_pub, profile_server_pub. Contributed certificates are prefixed with contrib_ (see below).

{ca_name} - CertificateAuthority

Certificate authority model without usable private key files.

Available for every CA generated in the test fixtures. Using this fixture enables database access.

{cert} - Certificate

Certificate model for certificates generated in test fixture data.

contrib_{ca_name} - CertificateAuthority

Certificate authority model for a contributed certificate.

Examples: contrib_godaddy_g2_root, contrib_geotrust and contrib_startssl_class3.

contrib_{ca_name}_cert - Certificate

Certificate model for contributed certificates loaded from test fixture data.

Examples: contrib_godaddy_g2_root_cert, contrib_geotrust_cert and contrib_startssl_class3_cert.

contrib_{ca_name}_cert_pub - Certificate

Certificate for contributed certificates loaded from test fixture data.

Examples: contrib_godaddy_g2_root_cert_pub, contrib_geotrust_cert_pub and contrib_startssl_class3_cert_pub.

contrib_{ca_name}_pub - Certificate

Certificate for contributed certificate authorities loaded from test fixture data.

Examples: contrib_godaddy_g2_root_pub, contrib_geotrust_pub and contrib_startssl_class3_pub.

usable_{ca_name} - CertificateAuthority

Certificate authority model with usable private key files.

Available for every CA generated in the test fixtures.

Mocks

Mocks used in the test suite.

django_ca.tests.base.mocks.mock_signal(signal: Signal) Iterator[Mock][source]

Context manager to attach a mock to the given Django signal.

Note that this mock does not mock that the signal is sent, but just attaches a mock to the Signal. Any other signal handler connected would still be called.

Assertions

django_ca.tests.base.assertions collects assertions used throughout the entire test suite.

django_ca.tests.base.assertions.assert_authority_key_identifier(issuer: CertificateAuthority, cert: X509CertMixin) None[source]

Assert the AuthorityKeyIdentifier extension of issuer.

This assertion tests that AuthorityKeyIdentifier extension of cert matches the SubjectKeyIdentifier extension of issuer.

django_ca.tests.base.assertions.assert_ca_properties(ca: ~django_ca.models.CertificateAuthority, name: str, parent: ~django_ca.models.CertificateAuthority | None = None, private_key_type: type[~cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey | ~cryptography.hazmat.primitives.asymmetric.ed448.Ed448PrivateKey | ~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey | ~cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKey | ~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey] = <class 'cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey'>, acme_enabled: bool = False, acme_profile: str | None = None, acme_requires_contact: bool = True, password: bytes | None = None) None[source]

Assert some basic properties of a CA.

django_ca.tests.base.assertions.assert_certificate(cert: ~cryptography.hazmat.bindings._rust.x509.Certificate | ~django_ca.models.CertificateAuthority | ~django_ca.models.Certificate, subject: ~cryptography.x509.name.Name, algorithm: type[~cryptography.hazmat.primitives.hashes.HashAlgorithm] = <class 'cryptography.hazmat.primitives.hashes.SHA512'>, signer: ~django_ca.models.CertificateAuthority | ~cryptography.hazmat.bindings._rust.x509.Certificate | None = None) None[source]

Assert certificate properties.

django_ca.tests.base.assertions.assert_command_error(msg: str, returncode: int = 1) Iterator[None][source]

Context manager asserting that CommandError is raised.

Parameters:
msgstr

The regex matching the exception message.

django_ca.tests.base.assertions.assert_count_equal(first: Iterable[Any], second: Iterable[Any]) None[source]

Roughly equivalent version of unittests assertCountEqual().

django_ca.tests.base.assertions.assert_create_ca_signals(pre: bool = True, post: bool = True) Iterator[tuple[Mock, Mock]][source]

Context manager asserting that the pre_create_ca/post_create_ca signals are (not) called.

django_ca.tests.base.assertions.assert_create_cert_signals(pre: bool = True, post: bool = True) Iterator[tuple[Mock, Mock]][source]

Context manager asserting that the pre_create_cert/post_create_cert signals are (not) called.

django_ca.tests.base.assertions.assert_crl(crl: bytes | ~cryptography.hazmat.bindings._rust.x509.CertificateRevocationList, expected: ~typing.Sequence[~django_ca.models.X509CertMixin] | None = None, signer: ~django_ca.models.CertificateAuthority | None = None, expires: int = 86400, algorithm: ~cryptography.hazmat.primitives.hashes.HashAlgorithm | None = None, encoding: ~cryptography.hazmat.primitives._serialization.Encoding = <Encoding.PEM: 'PEM'>, idp: ~cryptography.x509.extensions.Extension[~cryptography.x509.extensions.IssuingDistributionPoint] | None = None, extensions: list[~cryptography.x509.extensions.Extension[~cryptography.x509.extensions.ExtensionType]] | None = None, crl_number: int = 0, entry_extensions: tuple[list[~cryptography.x509.extensions.Extension[~cryptography.x509.extensions.ExtensionType]]] | None = None, last_update: ~datetime.datetime | None = None) None[source]

Test the given CRL.

Parameters:
crlbytes

The raw CRL

expectedlist
signer
expires
algorithm
encoding
idp
extensions
crl_number
django_ca.tests.base.assertions.assert_e2e_command_error(cmd: Sequence[str], stdout: str | bytes | Pattern = '', stderr: str | bytes | Pattern = '') None[source]

Assert that the passed command raises a CommandError with the given message.

django_ca.tests.base.assertions.assert_e2e_error(cmd: Sequence[str], stdout: str | bytes | Pattern = '', stderr: str | bytes | Pattern = '', code: int = 2) None[source]

Assert an error was through in an e2e command.

django_ca.tests.base.assertions.assert_extension_equal(first: Extension[ExtensionType] | None, second: Extension[ExtensionType] | None) None[source]

Compare two extensions for equality (or if both are None).

This assertion overrides comparison for iterable extension and should be used only when order of these extension values cannot be guaranteed. For example, two ExtendedKeyUsage extension will pass as equal regardless of order of the extended key usages in the extensions.

django_ca.tests.base.assertions.assert_extensions(cert: X509CertMixin | Certificate, extensions: Iterable[Extension[ExtensionType]], signer: CertificateAuthority | None = None, expect_defaults: bool = True) None[source]

Assert that cert has the given extensions.

django_ca.tests.base.assertions.assert_improperly_configured(msg: str) Iterator[None][source]

Shortcut for testing that the code raises ImproperlyConfigured with the given message.

django_ca.tests.base.assertions.assert_issuing_distribution_point(extension: Extension[IssuingDistributionPoint], full_name: Iterable[GeneralName] | None = None, relative_name: RelativeDistinguishedName | None = None, only_contains_user_certs: bool = False, only_contains_ca_certs: bool = False, only_some_reasons: frozenset[ReasonFlags] | None = None, indirect_crl: bool = False, only_contains_attribute_certs: bool = False, critical: bool = True) None[source]

Shortcut for asserting an Issuing Point Distribution extension.

django_ca.tests.base.assertions.assert_post_issue_cert(post: Mock, cert: Certificate) None[source]

Assert that the post_issue_cert signal was called with the expected certificate.

django_ca.tests.base.assertions.assert_removed_in_220(match: str | Pattern[str] | None = None) Iterator[None][source]

Assert that a RemovedInDjangoCA200Warning is emitted.

django_ca.tests.base.assertions.assert_removed_in_230(match: str | Pattern[str] | None = None) Iterator[None][source]

Assert that a RemovedInDjangoCA200Warning is emitted.

django_ca.tests.base.assertions.assert_revoked(cert: X509CertMixin, reason: str | None = None, compromised: datetime | None = None) None[source]

Assert that the certificate is now revoked.

django_ca.tests.base.assertions.assert_sign_cert_signals(pre: bool = True, post: bool = True) Iterator[tuple[Mock, Mock]][source]

Context manager mocking both pre_create_ca and post_create_ca signals.

django_ca.tests.base.assertions.assert_signature(chain: Iterable[CertificateAuthority], cert: Certificate | CertificateAuthority) None[source]

Assert that cert is properly signed by chain.

django_ca.tests.base.assertions.assert_system_exit(code: int) Iterator[None][source]

Assert that SystemExit is raised.

django_ca.tests.base.assertions.assert_validation_error(errors: dict[str, list[str]]) Iterator[None][source]

Context manager to assert that a ValidationError is thrown.

Admin interface

django_ca.tests.admin.assertions collects assertions used when testing the admin interface.

django_ca.tests.admin.assertions.assert_change_response(response: HttpResponse, media_css: tuple[tuple[str, str], ...] = ()) None[source]

Assert that the passed response is a model change view.

django_ca.tests.admin.assertions.assert_changelist_response(response: HttpResponse, *objects: Model) None[source]

Assert that the passed response is a model changelist view.

django_ca.tests.admin.assertions.assert_css(response: HttpResponse, path: str, media: str = 'all') None[source]

Assert that the HTML from the given response includes the mentioned CSS.

Utility functions

Utility functions used in testing.

class django_ca.tests.base.utils.DummyBackend(alias: str, **kwargs: Any)[source]

Backend with no actions whatsoever.

class django_ca.tests.base.utils.DummyModel[source]

Dummy model for the dummy backend.

django_ca.tests.base.utils.authority_information_access(ca_issuers: Iterable[GeneralName] | None = None, ocsp: Iterable[GeneralName] | None = None, critical: bool = False) Extension[AuthorityInformationAccess][source]

Shortcut for getting a AuthorityInformationAccess extension.

django_ca.tests.base.utils.basic_constraints(ca: bool = False, path_length: int | None = None, critical: bool = True) Extension[BasicConstraints][source]

Shortcut for getting a BasicConstraints extension.

django_ca.tests.base.utils.certificate_policies(*policies: PolicyInformation, critical: bool = False) Extension[CertificatePolicies][source]

Shortcut for getting a Certificate Policy extension.

django_ca.tests.base.utils.cmd(*args: Any, stdout: BytesIO, stderr: BytesIO, **kwargs: Any) tuple[bytes, bytes][source]
django_ca.tests.base.utils.cmd(*args: Any, stdout: BytesIO, stderr: StringIO | None = None, **kwargs: Any) tuple[bytes, str]
django_ca.tests.base.utils.cmd(*args: Any, stdout: StringIO | None = None, stderr: BytesIO, **kwargs: Any) tuple[str, bytes]
django_ca.tests.base.utils.cmd(*args: Any, stdout: StringIO | None = None, stderr: StringIO | None = None, **kwargs: Any) tuple[str, str]

Call to a manage.py command using call_command.

django_ca.tests.base.utils.cmd_e2e(args: Sequence[str], *, stdin: StringIO | bytes | None = None, stdout: StringIO | None = None, stderr: StringIO | None = None) tuple[str, str][source]
django_ca.tests.base.utils.cmd_e2e(args: Sequence[str], *, stdin: StringIO | bytes | None = None, stdout: BytesIO, stderr: StringIO | None = None) tuple[bytes, str]
django_ca.tests.base.utils.cmd_e2e(args: Sequence[str], *, stdin: StringIO | bytes | None = None, stdout: StringIO | None = None, stderr: BytesIO) tuple[str, bytes]
django_ca.tests.base.utils.cmd_e2e(args: Sequence[str], *, stdin: StringIO | bytes | None = None, stdout: BytesIO, stderr: BytesIO) tuple[bytes, bytes]

Call a management command the way manage.py does.

Unlike call_command, this method also tests the argparse configuration of the called command.

django_ca.tests.base.utils.cn(value: str) NameAttribute[source]

Shortcut for creating a common name attr.

django_ca.tests.base.utils.country(value: str) NameAttribute[source]

Shortcut for creating a country attr.

django_ca.tests.base.utils.crl_cache_key(serial: str, encoding: ~cryptography.hazmat.primitives._serialization.Encoding = <Encoding.DER: 'DER'>, only_contains_ca_certs: bool = False, only_contains_user_certs: bool = False, only_contains_attribute_certs: bool = False, only_some_reasons: ~collections.abc.Iterable[~cryptography.x509.extensions.ReasonFlags] | None = None) str[source]

Shortcut to get a CRL cache key.

django_ca.tests.base.utils.crl_distribution_points(*distribution_points: DistributionPoint, critical: bool = False) Extension[CRLDistributionPoints][source]

Shortcut for getting a CRLDistributionPoint extension.

django_ca.tests.base.utils.distribution_point(full_name: Iterable[GeneralName] | None = None, relative_name: RelativeDistinguishedName | None = None, reasons: frozenset[ReasonFlags] | None = None, crl_issuer: Iterable[GeneralName] | None = None) DistributionPoint[source]

Shortcut for generating a single distribution point.

django_ca.tests.base.utils.dns(name: str) DNSName[source]

Shortcut to get a cryptography.x509.DNSName.

django_ca.tests.base.utils.extended_key_usage(*usages: ObjectIdentifier, critical: bool = False) Extension[ExtendedKeyUsage][source]

Shortcut for getting an ExtendedKeyUsage extension.

django_ca.tests.base.utils.freshest_crl(*distribution_points: DistributionPoint, critical: bool = False) Extension[FreshestCRL][source]

Shortcut for getting a CRLDistributionPoints extension.

django_ca.tests.base.utils.get_cert_context(name: str) dict[str, Any][source]

Get a dictionary suitable for testing output based on the dictionary in basic.certs.

django_ca.tests.base.utils.get_idp(full_name: Iterable[GeneralName] | None = None, indirect_crl: bool = False, only_contains_attribute_certs: bool = False, only_contains_ca_certs: bool = False, only_contains_user_certs: bool = False, only_some_reasons: frozenset[ReasonFlags] | None = None, relative_name: RelativeDistinguishedName | None = None) Extension[IssuingDistributionPoint][source]

Get an IssuingDistributionPoint extension.

django_ca.tests.base.utils.ip(name: IPv4Address | IPv6Address | IPv4Network | IPv6Network) IPAddress[source]

Shortcut to get a cryptography.x509.IPAddress.

django_ca.tests.base.utils.iso_format(value: datetime, timespec: str = 'seconds') str[source]

Convert a timestamp to ISO, with ‘Z’ instead of ‘+00:00’.

django_ca.tests.base.utils.issuer_alternative_name(*names: GeneralName, critical: bool = False) Extension[IssuerAlternativeName][source]

Shortcut for getting a IssuerAlternativeName extension.

django_ca.tests.base.utils.key_usage(**usages: bool) Extension[KeyUsage][source]

Shortcut for getting a KeyUsage extension.

django_ca.tests.base.utils.mock_slug() Iterator[str][source]

Mock random slug generation, yields the static value.

django_ca.tests.base.utils.name_constraints(permitted: Iterable[GeneralName] | None = None, excluded: Iterable[GeneralName] | None = None, critical: bool = True) Extension[NameConstraints][source]

Shortcut for getting a NameConstraints extension.

django_ca.tests.base.utils.ocsp_no_check(critical: bool = False) Extension[OCSPNoCheck][source]

Shortcut for getting a OCSPNoCheck extension.

class django_ca.tests.base.utils.override_tmpcadir(**kwargs)[source]

Sets the CA_DIR directory to a temporary directory.

django_ca.tests.base.utils.precert_poison() Extension[PrecertPoison][source]

Shortcut for getting a PrecertPoison extension.

django_ca.tests.base.utils.rdn(name: Iterable[tuple[ObjectIdentifier, str]]) RelativeDistinguishedName[source]

Shortcut to get a cryptography.x509.RelativeDistinguishedName.

django_ca.tests.base.utils.root_reverse(name: str, **kwargs: Any) str[source]

Shortcut to get a django-ca URI with a root serial.

django_ca.tests.base.utils.root_uri(name: str, hostname: str | None = None, **kwargs: Any) str[source]

Full URI with a root serial.

django_ca.tests.base.utils.state(value: str) NameAttribute[source]

Return a state name attr.

django_ca.tests.base.utils.subject_alternative_name(*names: GeneralName, critical: bool = False) Extension[SubjectAlternativeName][source]

Shortcut for getting a SubjectAlternativeName extension.

django_ca.tests.base.utils.subject_key_identifier(cert: X509CertMixin | Certificate) Extension[SubjectKeyIdentifier][source]

Shortcut for getting a SubjectKeyIdentifier extension.

django_ca.tests.base.utils.tls_feature(*features: TLSFeatureType, critical: bool = False) Extension[TLSFeature][source]

Shortcut for getting a TLSFeature extension.

django_ca.tests.base.utils.uri(url: str) UniformResourceIdentifier[source]

Shortcut to get a cryptography.x509.UniformResourceIdentifier.

Doctests

django_ca.tests.base.doctest provides helper functions for testing doctests.

Functions in this module use a custom OutputChecker to enable the STRIP_WHITESPACE doctest option. This option will remove all whitespace (including newlines) from the both actual and expected output. It can be used for formatting actual output with newlines to improve readability. For example:

>>> from cryptography import x509
>>> from cryptography.x509.oid import ExtensionOID
>>> x509.Extension(
...     oid=ExtensionOID.BASIC_CONSTRAINTS,
...     critical=True,
...     value=x509.BasicConstraints(ca=False, path_length=None)
... )  
<Extension(
    oid=<ObjectIdentifier(oid=2.5.29.19, name=basicConstraints)>,
    critical=True,
    value=<BasicConstraints(ca=False, path_length=None)>
)>

Use django_ca.tests.base.doctest.doctest_module() to test a Python module:

def test_doctests() -> None:
    """Run doctests for this module."""
    failures, _tests = doctest_module("django_ca.pydantic.name")
    assert failures == 0, f"{failures} doctests failed, see above for output."

Helper functions for doctests.

django_ca.tests.base.doctest.doctest_module(module: str, name: str | None = None, globs: dict[str, str] | None = None, verbose: bool | None = False, report: bool = False, optionflags: int = 0, extraglobs: dict[str, str] | None = None, raise_on_error: bool = False, exclude_empty: bool = False) TestResults[source]

Shortcut for running doctests in the given Python module.

This function is based on doctest.testmod(). It differs in that it will add the STRIP_WHITESPACE doctest option and interpret module as module path if a str and import the module. The report and verbose flags also default to False, as this provides cleaner output in modules with a lot of doctests.