hbutils.testing.simulate.entry

CLI entry simulation utilities for test environments.

This module provides helpers for simulating the execution of command-line interface (CLI) entry functions within a controlled environment. It captures standard output, standard error, exit codes, and uncaught exceptions to support reproducible and assertion-friendly tests.

The module contains the following main components:

Example:

>>> from hbutils.testing.simulate.entry import simulate_entry
>>> def my_cli():
...     print("hello")
...
>>> result = simulate_entry(my_cli, argv=["mycli"])
>>> result.exitcode
0
>>> result.stdout
'hello\n'

Note

Uncaught exceptions raised by the entry function are captured in the EntryRunResult.error attribute instead of being printed to stderr.

__all__

hbutils.testing.simulate.entry.__all__ = ['simulate_entry', 'EntryRunResult']

Built-in mutable sequence.

If no argument is given, the constructor creates a new empty list. The argument must be an iterable if specified.

EntryRunResult

class hbutils.testing.simulate.entry.EntryRunResult(exitcode: int, stdout: str | None, stderr: str | None, error: BaseException | None)[source]

Result container for a simulated CLI entry invocation.

This class encapsulates the execution result of a CLI entry function, including exit code, captured stdout/stderr content, and any uncaught exception raised during execution.

Parameters:
  • exitcode (int) – Exit code returned by the entry function.

  • stdout (Optional[str]) – Output captured from standard output.

  • stderr (Optional[str]) – Output captured from standard error.

  • error (Optional[BaseException]) – Uncaught exception raised by the entry function.

__init__(exitcode: int, stdout: str | None, stderr: str | None, error: BaseException | None) None[source]

Initialize the entry run result.

Parameters:
  • exitcode (int) – Exit code returned by the entry function.

  • stdout (Optional[str]) – Output captured from standard output.

  • stderr (Optional[str]) – Output captured from standard error.

  • error (Optional[BaseException]) – Uncaught exception raised by the entry function.

assert_okay() None[source]

Assert that the entry execution was successful.

This checks that the exit code is 0 and no uncaught exception was raised. If the assertion fails, a detailed error message is provided.

Raises:

AssertionError – If the exit code is not 0 or an exception was raised.

Example:

>>> def my_cli():
...     print("hello")
...
>>> result = simulate_entry(my_cli, ['mycli'])
>>> result.assert_okay()
property error: BaseException | None

Uncaught exception raised inside the entry function.

Returns:

The exception object, or None if no exception was raised.

Return type:

Optional[BaseException]

property exitcode: int

Exit code returned by the entry function.

Returns:

The exit code value.

Return type:

int

property stderr: str | None

Output captured from standard error stream.

Returns:

The stderr content, or None if no output was captured.

Return type:

Optional[str]

property stdout: str | None

Output captured from standard output stream.

Returns:

The stdout content, or None if no output was captured.

Return type:

Optional[str]

simulate_entry

hbutils.testing.simulate.entry.simulate_entry(entry: Callable[[], Any], argv: List[str] | None = None, envs: Mapping[str, str] | None = None) EntryRunResult[source]

Simulate execution of a CLI entry function.

This function executes a CLI entry function in a controlled environment, capturing its output, exit code, and any uncaught exceptions for testing purposes.

Parameters:
  • entry (Callable[[], Any]) – Entry function; should be callable without arguments.

  • argv (Optional[List[str]]) – Command line arguments. If None, sys.argv is not mocked.

  • envs (Optional[Mapping[str, str]]) – Environment variables. If None, os.environ is not mocked.

Returns:

A result object containing exit code, stdout, stderr, and error.

Return type:

EntryRunResult

Examples::

We create a simple CLI code with click package, named test_cli1.py

 1 import sys
 2 import click
 3
 4 @click.command('cli1', help='CLI-1 example')
 5 @click.option('-c', type=int, help='optional C value', default=None)
 6 @click.argument('a', type=int)
 7 @click.argument('b', type=int)
 8 def cli1(a, b, c):
 9     if c is None:
10         print(f'{a} + {b} = {a + b}')
11     else:
12         print(f'{a} + {b} + {c} = {a + b + c}', file=sys.stderr)
13
14 if __name__ == '__main__':
15     cli1()

When we can try to simulate it.

>>> from hbutils.testing import simulate_entry
>>> from test_cli1 import cli1
>>> r1 = simulate_entry(cli1, ['cli1', '2', '3'])
>>> print(r1.exitcode)
0
>>> print(r1.stdout)
2 + 3 = 5
>>> r2 = simulate_entry(cli1, ['cli1', '2', '3', '-c', '24'])  # option
>>> print(r2.exitcode)
0
>>> print(r2.stderr)
2 + 3 + 24 = 29
>>> r3 = simulate_entry(cli1, ['cli', '--help'])  # help
>>> print(r3.stdout)
Usage: cli [OPTIONS] A B
  CLI-1 example
Options:
  -c INTEGER  optional C value
  --help      Show this message and exit.
>>> r4 = simulate_entry(cli1, ['cli', 'dklsfj'])  # misusage
>>> print(r4.exitcode)
2
>>> print(r4.stderr)
Usage: cli [OPTIONS] A B
Try 'cli --help' for help.
Error: Invalid value for 'A': 'dklsfj' is not a valid integer.

Note

If an uncaught exception is raised inside the entry function, it will be captured in EntryRunResult.error instead of being printed to stderr.

>>> from hbutils.testing import simulate_entry
>>> def my_cli():
...     raise ValueError(233)
>>>
>>> r = simulate_entry(my_cli)
>>> print(r.exitcode)  # will be 0x1
1
>>> print(r.stdout)  # nothing
>>> print(r.stderr)  # nothing as well
>>> print(repr(r.error))  # HERE!!!
ValueError(233)