hbutils.testing.generator

This module provides various test data generators for testing purposes.

It includes base generator classes, matrix-based generators, AETG (Automatic Efficient Test Generator) implementations, and functional utilities for generating test data. These generators are useful for creating comprehensive test cases and combinatorial testing scenarios.

The module exports: - BaseGenerator: Abstract base class for all generators - MatrixGenerator: Generator based on matrix operations - AETGGenerator: AETG algorithm-based test case generator - Functional utilities from the func submodule

BaseGenerator

class hbutils.testing.generator.BaseGenerator(values: Mapping[str, object], names: List[str] | None = None)[source]

Base generator class for creating test case combinations.

This class provides the foundation for generating test cases with different combinations of input values. It stores a mapping of parameter names to their possible values and provides methods to iterate over test cases in different formats.

Subclasses should implement the cases() method to define the specific strategy for generating test case combinations.

__init__(values: Mapping[str, object], names: List[str] | None = None)[source]

Initialize the BaseGenerator with values and optional names.

Parameters:
  • values (Mapping[str, object]) – A mapping of parameter names to their possible values. Each value can be a single item or an iterable (list, tuple, generator, range). Single values will be automatically converted to tuples. For example: {'a': [2, 3], 'b': ['x', 'y']}.

  • names (Optional[List[str]]) – Optional list of parameter names to define the order of parameters. If not provided, uses the sorted keys from values. Default is None.

Example::
>>> gen = BaseGenerator({'a': [1, 2], 'b': ['x', 'y']})
>>> gen.names
['a', 'b']
>>> gen.values
{'a': (1, 2), 'b': ('x', 'y')}
cases() Iterator[Mapping[str, object]][source]

Generate test cases as dictionaries.

This is a virtual method that must be implemented in subclasses to define the specific strategy for generating test case combinations.

Returns:

An iterator yielding dictionaries where keys are parameter names and values are the selected values for that test case.

Return type:

Iterator[Mapping[str, object]]

Raises:

NotImplementedError – This method must be implemented by subclasses.

Example::
>>> # In a subclass implementation:
>>> for case in generator.cases():
...     print(case)
{'a': 1, 'b': 'x'}
{'a': 2, 'b': 'y'}
property names: List[str]

Get the ordered list of parameter names.

Returns:

A list of parameter names in the order they should be used.

Return type:

List[str]

Example::
>>> gen = BaseGenerator({'b': [1, 2], 'a': [3, 4]})
>>> gen.names
['a', 'b']
tuple_cases() Iterator[Tuple[object, ...]][source]

Generate test cases as tuples.

This method converts the dictionary-based cases from cases() into tuples, with values ordered according to names. This format is convenient for use with testing frameworks that expect tuple arguments.

Returns:

An iterator yielding tuples of test case values in the order defined by names.

Return type:

Iterator[Tuple[object, …]]

Example::
>>> # Assuming cases() yields {'a': 1, 'b': 'x'} and {'a': 2, 'b': 'y'}
>>> # and names is ['a', 'b']
>>> for case in generator.tuple_cases():
...     print(case)
(1, 'x')
(2, 'y')
property values: Mapping[str, Tuple[object, ...]]

Get the selection values for test case generation.

Returns:

A mapping of parameter names to tuples of possible values.

Return type:

Mapping[str, Tuple[object, …]]

Example::
>>> gen = BaseGenerator({'a': [1, 2], 'b': 'x'})
>>> gen.values
{'a': (1, 2), 'b': ('x',)}

MatrixGenerator

class hbutils.testing.generator.MatrixGenerator(values: Mapping[str, Any], names: List[str] | None = None, includes: List[Mapping[str, Any]] | None = None, excludes: List[Mapping[str, Any]] | None = None)[source]

Full matrix model, all the cases in this matrix will be iterated.

This generator creates a cartesian product of all provided parameter values, with optional inclusions and exclusions to customize the generated test cases.

__init__(values: Mapping[str, Any], names: List[str] | None = None, includes: List[Mapping[str, Any]] | None = None, excludes: List[Mapping[str, Any]] | None = None)[source]

Constructor of the MatrixGenerator class.

It is similar to GitHub Action’s matrix strategy, generating all combinations of the provided values with support for custom inclusions and exclusions.

Parameters:
  • values (Mapping[str, Any]) – Matrix values, such as {'a': [2, 3], 'b': ['b', 'c']}.

  • names (Optional[List[str]]) – Names of the given generator, default is None which means use the sorted key set of the values.

  • includes (Optional[List[Mapping[str, Any]]]) – Include items, such as [{'a': 4, 'b': 'b'}], default is None which means no extra inclusions.

  • excludes (Optional[List[Mapping[str, Any]]]) – Exclude Items, such as [{'a': 2, 'b': 'c'}], default is None which means no extra exclusions.

Example::
>>> gen = MatrixGenerator(
...     {'a': [1, 2], 'b': ['x', 'y']},
...     includes=[{'a': 3, 'b': 'z'}],
...     excludes=[{'a': 1, 'b': 'x'}]
... )
cases() Iterator[Mapping[str, Any]][source]

Get the cases in this matrix.

Generates all possible combinations of the matrix values, applying exclusions and inclusions as specified. The method performs a cartesian product of all parameter values, then filters based on exclude rules, and finally adds any specified include cases.

Returns:

Iterator yielding dictionaries representing each test case.

Return type:

Iterator[Mapping[str, Any]]

Examples::
>>> from hbutils.testing import MatrixGenerator
>>> for p in MatrixGenerator(
...         {'a': [1, 2, 3], 'b': ['a', 'b'], 'r': [3, 4, 5]},
...         includes=[{'a': 4, 'r': 7}],
...         excludes=[{'a': 1, 'r': 3}, {'a': 3, 'b': 'b'}, {'a': 4, 'b': 'a', 'r': 7}]
... ).cases():
...     print(p)
{'a': 1, 'b': 'a', 'r': 4}
{'a': 1, 'b': 'a', 'r': 5}
{'a': 1, 'b': 'b', 'r': 4}
{'a': 1, 'b': 'b', 'r': 5}
{'a': 2, 'b': 'a', 'r': 3}
{'a': 2, 'b': 'a', 'r': 4}
{'a': 2, 'b': 'a', 'r': 5}
{'a': 2, 'b': 'b', 'r': 3}
{'a': 2, 'b': 'b', 'r': 4}
{'a': 2, 'b': 'b', 'r': 5}
{'a': 3, 'b': 'a', 'r': 3}
{'a': 3, 'b': 'a', 'r': 4}
{'a': 3, 'b': 'a', 'r': 5}
{'a': 4, 'b': 'b', 'r': 7}
property excludes: List[Mapping[str, Any]]

Get the exclude items.

Exclude items are parameter combinations that will be filtered out from the generated cases. A case is excluded if it matches all key-value pairs in any exclude item.

Returns:

List of exclude item mappings.

Return type:

List[Mapping[str, Any]]

property includes: List[Mapping[str, Any]]

Get the include items.

Include items are additional parameter combinations that will be added to the generated cases, even if they don’t fit the original matrix values.

Returns:

List of include item mappings.

Return type:

List[Mapping[str, Any]]

property names: List[str]

Get the ordered list of parameter names.

Returns:

A list of parameter names in the order they should be used.

Return type:

List[str]

Example::
>>> gen = BaseGenerator({'b': [1, 2], 'a': [3, 4]})
>>> gen.names
['a', 'b']
tuple_cases() Iterator[Tuple[object, ...]]

Generate test cases as tuples.

This method converts the dictionary-based cases from cases() into tuples, with values ordered according to names. This format is convenient for use with testing frameworks that expect tuple arguments.

Returns:

An iterator yielding tuples of test case values in the order defined by names.

Return type:

Iterator[Tuple[object, …]]

Example::
>>> # Assuming cases() yields {'a': 1, 'b': 'x'} and {'a': 2, 'b': 'y'}
>>> # and names is ['a', 'b']
>>> for case in generator.tuple_cases():
...     print(case)
(1, 'x')
(2, 'y')
property values: Mapping[str, Tuple[object, ...]]

Get the selection values for test case generation.

Returns:

A mapping of parameter names to tuples of possible values.

Return type:

Mapping[str, Tuple[object, …]]

Example::
>>> gen = BaseGenerator({'a': [1, 2], 'b': 'x'})
>>> gen.values
{'a': (1, 2), 'b': ('x',)}

AETGGenerator

class hbutils.testing.generator.AETGGenerator(values, names: List[str] | None = None, pairs: List[Tuple[str, ...]] | None = None, rnd: Random | int | None = None)[source]

Full AETG model, test cases will be generated to make sure the required pairs will be all tested.

The AETG (Automatic Efficient Test Generator) algorithm generates test cases that ensure all required parameter combinations are covered. It uses a greedy approach to select parameter values that cover the most uncovered pairs.

Example::
>>> from hbutils.testing import AETGGenerator
>>> gene = AETGGenerator({'a': (1, 2), 'b': (3, 4), 'c': (5, 6)})
>>> for case in gene.cases():
...     print(case)
{'a': 1, 'b': 3, 'c': 5}
{'a': 2, 'b': 4, 'c': 6}
...
__init__(values, names: List[str] | None = None, pairs: List[Tuple[str, ...]] | None = None, rnd: Random | int | None = None)[source]

Constructor of the hbutils.testing.AETGGenerator class.

Parameters:
  • values (Mapping[str, Iterable]) – Selection values, such as {'a': [2, 3], 'b': ['b', 'c']}.

  • names (Optional[List[str]]) – Names of the given generator, default is None which means use the sorted key set of the values.

  • pairs (Optional[List[Tuple[str, ...]]]) – Pairs required to be all tested, default is None which means all the binary pairs will be included.

  • rnd (Optional[Union[random.Random, int]]) – Random object or int-formatted random seed, default is None which means the default random object will be used.

cases() Iterator[Mapping[str, object]][source]

Get the cases in this AETG model.

Generates test cases using the AETG algorithm, ensuring all required parameter combinations are covered. The algorithm uses a greedy approach to select values that cover the most uncovered pairs.

Returns:

Iterator yielding test case dictionaries.

Return type:

Iterator[Mapping[str, object]]

Examples::
>>> from hbutils.testing import AETGGenerator
>>> gene = AETGGenerator({'a': (1, 2), 'b': (3, 4), 'c': (5, 6), 'd': (7, 8), 'e': (9, 10)})
>>> for p in gene.cases():
...     print(p)
{'a': 1, 'b': 3, 'c': 6, 'd': 8, 'e': 10}
{'a': 2, 'b': 4, 'c': 5, 'd': 7, 'e': 9}
{'a': 2, 'b': 3, 'c': 6, 'd': 7, 'e': 9}
{'a': 2, 'b': 4, 'c': 6, 'd': 8, 'e': 10}
{'a': 1, 'b': 3, 'c': 5, 'd': 7, 'e': 10}
{'a': 1, 'b': 4, 'c': 5, 'd': 8, 'e': 9}
>>> gene = AETGGenerator(
...     {'a': (1, 2), 'b': (3, 4), 'c': (5, 6), 'd': (7, 8), 'e': (9, 10)},
...     pairs=[('a', 'c'), ('b', 'd'), ('e',)]
... )
>>> for p in gene.cases():
...     print(p)
{'a': 2, 'b': 3, 'c': 6, 'd': 8, 'e': 9}
{'a': 1, 'b': 4, 'c': 5, 'd': 7, 'e': 10}
{'a': 2, 'b': 4, 'c': 5, 'd': 8, 'e': 9}
{'a': 1, 'b': 3, 'c': 6, 'd': 7, 'e': 10}
property names: List[str]

Get the ordered list of parameter names.

Returns:

A list of parameter names in the order they should be used.

Return type:

List[str]

Example::
>>> gen = BaseGenerator({'b': [1, 2], 'a': [3, 4]})
>>> gen.names
['a', 'b']
property pairs: List[Tuple[str, ...]]

Pairs required to be all tested.

Returns:

List of parameter name tuples that must be covered.

Return type:

List[Tuple[str, …]]

tuple_cases() Iterator[Tuple[object, ...]]

Generate test cases as tuples.

This method converts the dictionary-based cases from cases() into tuples, with values ordered according to names. This format is convenient for use with testing frameworks that expect tuple arguments.

Returns:

An iterator yielding tuples of test case values in the order defined by names.

Return type:

Iterator[Tuple[object, …]]

Example::
>>> # Assuming cases() yields {'a': 1, 'b': 'x'} and {'a': 2, 'b': 'y'}
>>> # and names is ['a', 'b']
>>> for case in generator.tuple_cases():
...     print(case)
(1, 'x')
(2, 'y')
property values: Mapping[str, Tuple[object, ...]]

Get the selection values for test case generation.

Returns:

A mapping of parameter names to tuples of possible values.

Return type:

Mapping[str, Tuple[object, …]]

Example::
>>> gen = BaseGenerator({'a': [1, 2], 'b': 'x'})
>>> gen.values
{'a': (1, 2), 'b': ('x',)}

tmatrix

hbutils.testing.generator.tmatrix(ranges: Mapping[str | Tuple[str, ...], List], mode='aetg', seed: int | None = 0, level: int = 2) Tuple[List[str], List[Tuple]][source]

Generate test matrix for parameterized testing.

This function creates a test matrix that can be directly used with pytest’s parametrize decorator. It supports two generation modes: AETG for efficient combinatorial testing and MATRIX for full Cartesian product generation.

Parameters:
  • ranges (Mapping[Union[str, Tuple[str, ...]], List]) – Mapping of parameter names to their possible values. Keys can be either a single string (for one parameter) or a tuple of strings (for multiple parameters that should be varied together). Values are lists of possible values for the parameter(s).

  • mode (str) – Generation mode, should be either ‘aetg’ or ‘matrix’. Default is ‘aetg’.

  • seed (Optional[int]) – Random seed for AETG mode. Default is 0 which produces deterministic results. Set to None for non-deterministic generation.

  • level (int) – Coverage level for AETG algorithm, indicating the strength of combinatorial coverage (e.g., 2 for pairwise coverage). Default is 2.

Returns:

A tuple containing (parameter_names, test_cases) where parameter_names is a list of parameter names and test_cases is a list of tuples, each representing one test case.

Return type:

Tuple[List[str], List[Tuple]]

Raises:

ValueError – If an invalid mode is specified.

Examples::
>>> from hbutils.testing import tmatrix
>>> names, values = tmatrix(
...     {
...         'a': [2, 3],
...         'e': ['a', 'b', 'c'],
...         ('b', 'c'): [(1, 7), (4, 6), (9, 12)],
...     }
... )
>>> print(names)
['a', 'e', 'b', 'c']
>>> for i, v in enumerate(values):
...     print(i, v)
0 (2, 'c', 9, 12)
1 (3, 'c', 4, 6)
2 (2, 'c', 1, 7)
3 (3, 'b', 9, 12)
4 (2, 'b', 4, 6)
5 (3, 'b', 1, 7)
6 (3, 'a', 9, 12)
7 (2, 'a', 4, 6)
8 (3, 'a', 1, 7)

Note

This can be directly used in pytest.mark.parametrize function.

>>> @pytest.mark.unittest
... class TestTestingGeneratorFunc:
...     @pytest.mark.parametrize(*tmatrix({
...         'a': [2, 3],
...         'e': ['a', 'b', 'c'],
...         ('b', 'c'): [(1, 7), (4, 6), (9, 12)],
...     }))
...     def test_tmatrix_usage(self, a, e, b, c):
...         print(a, e, b, c)