Skip to content

Commons

qa_pytest_commons

__all__ = ['AbstractTestsBase', 'BaseConfiguration', 'BddKeywords', 'Configuration', 'GenericSteps'] module-attribute

AbstractTestsBase

Bases: ABC, LoggerMixin, ImmutableMixin

Basic test scenario implementation, holding some type of steps and a logger facility.

Subtypes must set _steps_type to the actual type of steps implementation::

                    +---------------+
                    |  BddKeyWords  |
                    +---------------+
                                    ^
                                    |
                                implements
                                    |
+-------------------+               +--------------+
| AbstractTestsBase |---contains--->| GenericSteps |
|                   |               +--------------+
|                   |                       +---------------+
|                   |---contains----------->| Configuration |
+-------------------+                       +---------------+

IMPORTANT: pytest classes must not define an init method.

Type Parameters

TSteps (TSteps:GenericSteps): The actual steps implementation, or partial implementation. TConfiguration (TConfiguration:Configuration): The configuration type for the test scenario.

Attributes:

Name Type Description
_steps_type Type[TSteps]

The type of the steps implementation. Must be set by subtypes.

_configuration TConfiguration

The configuration instance for the test scenario. Must be set by subtypes.

Source code in qa-pytest-commons/src/qa_pytest_commons/abstract_tests_base.py
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
class AbstractTestsBase[
        TSteps: GenericSteps[Any],
        TConfiguration: Configuration](ABC, LoggerMixin, ImmutableMixin):
    """
    Basic test scenario implementation, holding some type of steps and a logger
    facility.

    Subtypes must set `_steps_type` to the actual type of steps implementation::

                            +---------------+
                            |  BddKeyWords  |
                            +---------------+
                                            ^
                                            |
                                        implements
                                            |
        +-------------------+               +--------------+
        | AbstractTestsBase |---contains--->| GenericSteps |
        |                   |               +--------------+
        |                   |                       +---------------+
        |                   |---contains----------->| Configuration |
        +-------------------+                       +---------------+

    IMPORTANT: pytest classes must not define an __init__ method.

    Type Parameters:
        TSteps (TSteps:GenericSteps): The actual steps implementation, or partial implementation.
        TConfiguration (TConfiguration:Configuration): The configuration type for the test scenario.

    Attributes:
        _steps_type (Type[TSteps]): The type of the steps implementation. Must be set by subtypes.
        _configuration (TConfiguration): The configuration instance for the test scenario. Must be set by subtypes.
    """
    _steps_type: Type[TSteps]
    _configuration: TConfiguration

    @property
    def configuration(self) -> TConfiguration:
        '''
        Returns the configuration instance.

        Returns:
            TConfiguration: The configuration instance.
        '''
        return self._configuration

    @final
    @cached_property
    def steps(self) -> TSteps:
        '''
        Lazily initializes and returns an instance of steps implementation.

        Returns:
            TSteps: The instance of steps implementation.
        '''
        self.log.debug(f"initiating {self._steps_type}")
        return self._steps_type(self._configuration)

    def setup_method(self):
        """
        Override in subtypes with specific setup, if any.
        """
        self.log.debug("setup")

    def teardown_method(self):
        """
        Override in subtypes with specific teardown, if any.
        """
        self.log.debug("teardown")

configuration property

Returns the configuration instance.

Returns:

Name Type Description
TConfiguration TConfiguration

The configuration instance.

steps cached property

Lazily initializes and returns an instance of steps implementation.

Returns:

Name Type Description
TSteps TSteps

The instance of steps implementation.

setup_method()

Override in subtypes with specific setup, if any.

Source code in qa-pytest-commons/src/qa_pytest_commons/abstract_tests_base.py
73
74
75
76
77
def setup_method(self):
    """
    Override in subtypes with specific setup, if any.
    """
    self.log.debug("setup")

teardown_method()

Override in subtypes with specific teardown, if any.

Source code in qa-pytest-commons/src/qa_pytest_commons/abstract_tests_base.py
79
80
81
82
83
def teardown_method(self):
    """
    Override in subtypes with specific teardown, if any.
    """
    self.log.debug("teardown")

BaseConfiguration

Bases: Configuration, LoggerMixin, ImmutableMixin

Base class for all types of configurations, providing a parser for a pre-specified configuration file.

Source code in qa-pytest-commons/src/qa_pytest_commons/base_configuration.py
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
class BaseConfiguration(Configuration, LoggerMixin, ImmutableMixin):
    """
    Base class for all types of configurations, providing a parser for a pre-specified configuration file.
    """
    _path: Path

    def __init__(self, path: Path | None = None):
        """
        Initializes the configuration by loading the associated `.ini` file.

        If `path` is not provided, the file is inferred based on the module name
        of the subclass and loaded from a structured configuration directory.

        The default lookup path follows this structure:
            <module_dir>/configurations/${TEST_ENVIRONMENT}/<module_name>.ini

        Where:
            - <module_dir> is the directory where the subclass's module is located
            - ${TEST_ENVIRONMENT} is an optional environment variable that specifies
            the subdirectory (e.g., "dev", "ci", "prod"). If unset, it defaults
            to an empty string (i.e., no subdirectory)
            - <module_name> is the name of the `.py` file defining the subclass

        Args:
            path (Path, optional): Explicit path to the configuration file. If provided,
                                overrides automatic inference.

        Raises:
            FileNotFoundError: If the resolved configuration file does not exist.
        """
        if path is None:
            module_file = Path(inspect.getfile(self.__class__))
            module_stem = module_file.stem
            resources_dir = module_file.parent / "configurations" / \
                os.environ.get("TEST_ENVIRONMENT", EMPTY_STRING)
            ini_file = resources_dir / f"{module_stem}.ini"
            self._path = ini_file
        else:
            self._path = path

        if not self._path.exists():
            raise FileNotFoundError(
                f"configuration file not found: {self._path.resolve()}")

        self.log.debug(f"using configuration from {self._path}")

    # NOTE if properties cannot be cached, this is a red-flag
    # configuration properties should be immutable.
    @final
    @cached_property
    def parser(self) -> configparser.ConfigParser:
        """
        Parser that reads this configuration.
        """
        self.log.debug(f"reading configuration from {self._path}")
        parser = configparser.ConfigParser()
        config_files = parser.read(self._path)
        self.log.debug(f"successfully read {config_files}")
        return parser

parser cached property

Parser that reads this configuration.

__init__(path=None)

Initializes the configuration by loading the associated .ini file.

If path is not provided, the file is inferred based on the module name of the subclass and loaded from a structured configuration directory.

The default lookup path follows this structure

/configurations/${TEST_ENVIRONMENT}/.ini

Where
  • is the directory where the subclass's module is located
  • ${TEST_ENVIRONMENT} is an optional environment variable that specifies the subdirectory (e.g., "dev", "ci", "prod"). If unset, it defaults to an empty string (i.e., no subdirectory)
  • is the name of the .py file defining the subclass

Parameters:

Name Type Description Default
path Path

Explicit path to the configuration file. If provided, overrides automatic inference.

None

Raises:

Type Description
FileNotFoundError

If the resolved configuration file does not exist.

Source code in qa-pytest-commons/src/qa_pytest_commons/base_configuration.py
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
def __init__(self, path: Path | None = None):
    """
    Initializes the configuration by loading the associated `.ini` file.

    If `path` is not provided, the file is inferred based on the module name
    of the subclass and loaded from a structured configuration directory.

    The default lookup path follows this structure:
        <module_dir>/configurations/${TEST_ENVIRONMENT}/<module_name>.ini

    Where:
        - <module_dir> is the directory where the subclass's module is located
        - ${TEST_ENVIRONMENT} is an optional environment variable that specifies
        the subdirectory (e.g., "dev", "ci", "prod"). If unset, it defaults
        to an empty string (i.e., no subdirectory)
        - <module_name> is the name of the `.py` file defining the subclass

    Args:
        path (Path, optional): Explicit path to the configuration file. If provided,
                            overrides automatic inference.

    Raises:
        FileNotFoundError: If the resolved configuration file does not exist.
    """
    if path is None:
        module_file = Path(inspect.getfile(self.__class__))
        module_stem = module_file.stem
        resources_dir = module_file.parent / "configurations" / \
            os.environ.get("TEST_ENVIRONMENT", EMPTY_STRING)
        ini_file = resources_dir / f"{module_stem}.ini"
        self._path = ini_file
    else:
        self._path = path

    if not self._path.exists():
        raise FileNotFoundError(
            f"configuration file not found: {self._path.resolve()}")

    self.log.debug(f"using configuration from {self._path}")

BddKeywords

Bases: ABC

Base class for defining Behavior-Driven Development (BDD) keywords.

This class provides a set of properties that represent the common BDD keywords such as given, when, then, and_, with_. Implementations might be of two types: step implementations (GenericSteps) or scenario implementations (AbstractTestsBase). In both cases, these properties must return an object that provides same step implementation, allowing a fluent-style coding.

Type Parameters

TSteps (TSteps:BddKeywords): The actual steps implementation, or partial implementation.

Source code in qa-pytest-commons/src/qa_pytest_commons/bdd_keywords.py
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
class BddKeywords[TSteps:BddKeywords](ABC):
    """
    Base class for defining Behavior-Driven Development (BDD) keywords.

    This class provides a set of properties that represent the common BDD keywords
    such as `given`, `when`, `then`, `and_`, `with_`. Implementations might be
    of two types: step implementations (GenericSteps) or scenario implementations
    (AbstractTestsBase). In both cases, these properties must return an object
    that provides same step implementation, allowing a fluent-style coding.

    Type Parameters:
        TSteps (TSteps:BddKeywords): The actual steps implementation, or partial implementation.
    """

    @property
    @abstractmethod
    def given(self) -> TSteps:
        """
        Use to start definition of given stage.

        The given stage is the start-up point of a test.
        """
        pass

    @property
    @abstractmethod
    def when(self) -> TSteps:
        """
        Use to start definition of operations stage.

        The operations stage is the part that triggers some behavior on the SUT.
        """
        pass

    @property
    @abstractmethod
    def then(self) -> TSteps:
        """
        Use to start definition of verifications stage.

        The verifications stage is the part that samples actual output of the
        SUT and compares it against a predefined condition (a.k.a. rule).
        """
        pass

    @property
    @abstractmethod
    def and_(self) -> TSteps:
        """
        Use to continue definition of previous stage.
        """
        pass

    @property
    @abstractmethod
    def with_(self) -> TSteps:
        """
        Same as `and_`, sometimes it just sounds better.
        """
        pass

and_ abstractmethod property

Use to continue definition of previous stage.

given abstractmethod property

Use to start definition of given stage.

The given stage is the start-up point of a test.

then abstractmethod property

Use to start definition of verifications stage.

The verifications stage is the part that samples actual output of the SUT and compares it against a predefined condition (a.k.a. rule).

when abstractmethod property

Use to start definition of operations stage.

The operations stage is the part that triggers some behavior on the SUT.

with_ abstractmethod property

Same as and_, sometimes it just sounds better.

Configuration

Empty configuration base class for scenarios that do not require configuration.

Source code in qa-pytest-commons/src/qa_pytest_commons/base_configuration.py
17
18
19
20
21
class Configuration():
    """
    Empty configuration base class for scenarios that do not require configuration.
    """
    pass

GenericSteps

Bases: BddKeywords['GenericSteps'], LoggerMixin, ImmutableMixin

Generic steps base class for BDD-style test implementations. Provides retrying, assertion, and step chaining utilities for all step types.

Type Parameters

TConfiguration: The configuration type for the steps implementation.

Attributes:

Name Type Description
_retrying Retrying

The tenacity.Retrying instance used for retry logic.

_configuration TConfiguration

The configuration instance for these steps.

Source code in qa-pytest-commons/src/qa_pytest_commons/generic_steps.py
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
class GenericSteps[TConfiguration: BaseConfiguration](
        BddKeywords['GenericSteps'],
        LoggerMixin,
        ImmutableMixin):
    """
    Generic steps base class for BDD-style test implementations.
    Provides retrying, assertion, and step chaining utilities for all step types.

    Type Parameters:
        TConfiguration: The configuration type for the steps implementation.

    Attributes:
        _retrying (Retrying): The tenacity.Retrying instance used for retry logic.
        _configuration (TConfiguration): The configuration instance for these steps.
    """

    _retrying: Retrying
    _configuration: TConfiguration

    def __init__(self, configuration: TConfiguration):
        """
        Initializes the steps with the given configuration and default retry policy.

        Args:
            configuration (TConfiguration): The configuration instance.
        """
        self._configuration = configuration
        # NOTE: waits 1 sec after 1st failure, 2, 4, and 8 secs on subsequent;
        # see BddScenarioTests#should_retry
        self._retrying = Retrying(
            stop=stop_after_attempt(4),
            wait=wait_exponential(min=1, max=10),
            retry=retry_if_exception_type(Exception),
            before_sleep=before_sleep_log(self.log, logging.DEBUG)
        )

    @final
    @property
    def configured(self) -> TConfiguration:
        """
        Returns the configuration instance for these steps.

        Returns:
            TConfiguration: The configuration instance.
        """
        return self._configuration

    @final
    @property
    def retry_policy(self) -> Retrying:
        """
        Returns the retry policy used for retrying steps.

        Returns:
            Retrying: The tenacity.Retrying instance.
        """
        return self._retrying

    @final
    @property
    @override
    def given(self) -> Self:
        Context.set(lambda m: f"Given {m}")
        return self

    @final
    @property
    @override
    def when(self) -> Self:
        Context.set(lambda m: f"When {m}")
        return self

    @final
    @property
    @override
    def then(self) -> Self:
        Context.set(lambda m: f"Then {m}")
        return self

    @final
    @property
    @override
    def and_(self) -> Self:
        Context.set(lambda m: f"And {m}")
        return self

    @final
    @property
    @override
    def with_(self) -> Self:
        Context.set(lambda m: f"With {m}")
        return self

    @final
    @property
    @Context.traced
    def nothing(self) -> Self:
        """
        Intended to support self-testing which does not rely on outer world system.

        Returns:
            Self: these steps
        """
        return self

    # DELETEME
    # # @Context.traced -- nothing to trace here...
    # def configuration(self, configuration: TConfiguration) -> Self:
    #     """
    #     Sets the configuration to use.

    #     Args:
    #         configuration (TConfiguration): the configuration

    #     Returns:
    #         Self: these steps
    #     """
    #     self._configuration = configuration
    #     return self

    def set[T:Valid](self, field_name: str, field_value: T) -> T:
        """
        Sets field to specified value, validating it if possible.

        Args:
            field_name (str): name of field; the field should be defined as annotation
            field_value (T:Valid): value of field that can be validated

        Raises:
            AttributeError: if the field is not defined
            TypeError: if the object does not support the Valid protocol
            InvalidValueException: if the object is invalid

        Returns:
            T: the value of set field
        """
        if field_name not in self.__class__.__annotations__:
            raise AttributeError(
                f"{field_name} is not a valid attribute of "
                f"{self.__class__.__name__}.")

        setattr(self, field_name, valid(field_value))
        return field_value

    @final
    def step(self, *args: Any) -> Self:
        """
        Casts anything to a step.

        Returns:
            Self: these steps
        """
        return self

    @final
    def tracing(self, value: Any) -> Self:
        """
        Logs value at DEBUG level using the logger of this steps class.

        Args:
            value (Any): The value to log.
        Returns:
            Self: these steps
        """
        self.log.debug(f"=== {value}")
        return self

    @final
    @Context.traced
    def waiting(self, duration: timedelta = timedelta(seconds=0)) -> Self:
        """
        Blocks current thread for specified duration.

        Args:
            duration (timedelta, optional): How long to wait. Defaults to 0 seconds.
        Returns:
            Self: these steps
        """
        sleep_for(duration)
        return self

    @final
    @Context.traced
    def failing(self, exception: Exception) -> Self:
        """
        Raises the given exception, for self-testing of retrying and eventually_assert_that.

        Args:
            exception (Exception): The exception to raise.
        Raises:
            exception: That exception.
        Returns:
            Self: these steps
        """
        raise exception

    @final
    @Context.traced
    def repeating(self, range: range, step: Callable[[int], Self]) -> Self:
        """
        Repeats the specified step for each value in the range.

        Args:
            range (range): The range to iterate over.
            step (Callable[[int], Self]): The step to repeat.
        Returns:
            Self: these steps
        """
        seq(range).for_each(step)  # type: ignore
        return self

    # TODO parallel_repeating

    @final
    @Context.traced
    def safely(self, step: Callable[[], Self]) -> Self:
        """
        Executes specified step, swallowing its exceptions.

        Args:
            step (Callable[[], Self]): The step to execute.
        Returns:
            Self: these steps
        """
        return safely(lambda: step()).value_or(self)

    # TODO implement a raises decorator to mark method as raising some exception
    # at run-time the decorator shall check if raised exception matches the declared list.
    # This one would be:
    # @raises(tenacity.RetryError)
    @final
    # @Context.traced
    def retrying(self, step: Callable[[], Self]) -> Self:
        '''
        Retries specified step according to _retry_policy.

        Args:
            step (Callable[[], Self]): The step to retry.
        Returns:
            Self: these steps
        '''
        return self._retrying(step)

    @final
    def eventually_assert_that[T](
            self, supplier: Supplier[T],
            by_rule: Matcher[T]) -> Self:
        '''
        Repeatedly applies specified rule on specified supplier, according to _retry_policy.

        Args:
            supplier (Callable[[], T]): The value supplier.
            by_rule (Matcher[T]): The matcher to apply.
        Returns:
            Self: these steps
        '''
        return self._retrying(lambda: self._assert_that(supplier(), by_rule))

    @final
    @Context.traced
    def it_works(self, matcher: Matcher[bool]) -> Self:
        """
        Intended to support self-testing of reports.

        Args:
            matcher (Matcher[bool]): Matcher for the boolean result.
        Returns:
            Self: these steps
        """
        assert_that(True, matcher)
        return self

    @final
    # NOTE @Context.traced here is redundant
    def _assert_that[T](self, value: T, by_rule: Matcher[T]) -> Self:
        """
        Adapts PyHamcrest's assert_that to the BDD world by returning Self.

        Args:
            value (T): The value to assert upon.
            by_rule (Matcher[T]): The matcher to apply.
        Returns:
            Self: these steps
        """
        assert_that(value, by_rule)
        return self

and_ property

configured property

Returns the configuration instance for these steps.

Returns:

Name Type Description
TConfiguration TConfiguration

The configuration instance.

given property

nothing property

Intended to support self-testing which does not rely on outer world system.

Returns:

Name Type Description
Self Self

these steps

retry_policy property

Returns the retry policy used for retrying steps.

Returns:

Name Type Description
Retrying Retrying

The tenacity.Retrying instance.

then property

when property

with_ property

__init__(configuration)

Initializes the steps with the given configuration and default retry policy.

Parameters:

Name Type Description Default
configuration TConfiguration

The configuration instance.

required
Source code in qa-pytest-commons/src/qa_pytest_commons/generic_steps.py
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
def __init__(self, configuration: TConfiguration):
    """
    Initializes the steps with the given configuration and default retry policy.

    Args:
        configuration (TConfiguration): The configuration instance.
    """
    self._configuration = configuration
    # NOTE: waits 1 sec after 1st failure, 2, 4, and 8 secs on subsequent;
    # see BddScenarioTests#should_retry
    self._retrying = Retrying(
        stop=stop_after_attempt(4),
        wait=wait_exponential(min=1, max=10),
        retry=retry_if_exception_type(Exception),
        before_sleep=before_sleep_log(self.log, logging.DEBUG)
    )

eventually_assert_that(supplier, by_rule)

Repeatedly applies specified rule on specified supplier, according to _retry_policy.

Parameters:

Name Type Description Default
supplier Callable[[], T]

The value supplier.

required
by_rule Matcher[T]

The matcher to apply.

required

Returns: Self: these steps

Source code in qa-pytest-commons/src/qa_pytest_commons/generic_steps.py
271
272
273
274
275
276
277
278
279
280
281
282
283
284
@final
def eventually_assert_that[T](
        self, supplier: Supplier[T],
        by_rule: Matcher[T]) -> Self:
    '''
    Repeatedly applies specified rule on specified supplier, according to _retry_policy.

    Args:
        supplier (Callable[[], T]): The value supplier.
        by_rule (Matcher[T]): The matcher to apply.
    Returns:
        Self: these steps
    '''
    return self._retrying(lambda: self._assert_that(supplier(), by_rule))

failing(exception)

Raises the given exception, for self-testing of retrying and eventually_assert_that.

Parameters:

Name Type Description Default
exception Exception

The exception to raise.

required

Raises: exception: That exception. Returns: Self: these steps

Source code in qa-pytest-commons/src/qa_pytest_commons/generic_steps.py
209
210
211
212
213
214
215
216
217
218
219
220
221
222
@final
@Context.traced
def failing(self, exception: Exception) -> Self:
    """
    Raises the given exception, for self-testing of retrying and eventually_assert_that.

    Args:
        exception (Exception): The exception to raise.
    Raises:
        exception: That exception.
    Returns:
        Self: these steps
    """
    raise exception

it_works(matcher)

Intended to support self-testing of reports.

Parameters:

Name Type Description Default
matcher Matcher[bool]

Matcher for the boolean result.

required

Returns: Self: these steps

Source code in qa-pytest-commons/src/qa_pytest_commons/generic_steps.py
286
287
288
289
290
291
292
293
294
295
296
297
298
@final
@Context.traced
def it_works(self, matcher: Matcher[bool]) -> Self:
    """
    Intended to support self-testing of reports.

    Args:
        matcher (Matcher[bool]): Matcher for the boolean result.
    Returns:
        Self: these steps
    """
    assert_that(True, matcher)
    return self

repeating(range, step)

Repeats the specified step for each value in the range.

Parameters:

Name Type Description Default
range range

The range to iterate over.

required
step Callable[[int], Self]

The step to repeat.

required

Returns: Self: these steps

Source code in qa-pytest-commons/src/qa_pytest_commons/generic_steps.py
224
225
226
227
228
229
230
231
232
233
234
235
236
237
@final
@Context.traced
def repeating(self, range: range, step: Callable[[int], Self]) -> Self:
    """
    Repeats the specified step for each value in the range.

    Args:
        range (range): The range to iterate over.
        step (Callable[[int], Self]): The step to repeat.
    Returns:
        Self: these steps
    """
    seq(range).for_each(step)  # type: ignore
    return self

retrying(step)

Retries specified step according to _retry_policy.

Parameters:

Name Type Description Default
step Callable[[], Self]

The step to retry.

required

Returns: Self: these steps

Source code in qa-pytest-commons/src/qa_pytest_commons/generic_steps.py
258
259
260
261
262
263
264
265
266
267
268
269
@final
# @Context.traced
def retrying(self, step: Callable[[], Self]) -> Self:
    '''
    Retries specified step according to _retry_policy.

    Args:
        step (Callable[[], Self]): The step to retry.
    Returns:
        Self: these steps
    '''
    return self._retrying(step)

safely(step)

Executes specified step, swallowing its exceptions.

Parameters:

Name Type Description Default
step Callable[[], Self]

The step to execute.

required

Returns: Self: these steps

Source code in qa-pytest-commons/src/qa_pytest_commons/generic_steps.py
241
242
243
244
245
246
247
248
249
250
251
252
@final
@Context.traced
def safely(self, step: Callable[[], Self]) -> Self:
    """
    Executes specified step, swallowing its exceptions.

    Args:
        step (Callable[[], Self]): The step to execute.
    Returns:
        Self: these steps
    """
    return safely(lambda: step()).value_or(self)

set(field_name, field_value)

Sets field to specified value, validating it if possible.

Parameters:

Name Type Description Default
field_name str

name of field; the field should be defined as annotation

required
field_value T

Valid): value of field that can be validated

required

Raises:

Type Description
AttributeError

if the field is not defined

TypeError

if the object does not support the Valid protocol

InvalidValueException

if the object is invalid

Returns:

Name Type Description
T T

the value of set field

Source code in qa-pytest-commons/src/qa_pytest_commons/generic_steps.py
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
def set[T:Valid](self, field_name: str, field_value: T) -> T:
    """
    Sets field to specified value, validating it if possible.

    Args:
        field_name (str): name of field; the field should be defined as annotation
        field_value (T:Valid): value of field that can be validated

    Raises:
        AttributeError: if the field is not defined
        TypeError: if the object does not support the Valid protocol
        InvalidValueException: if the object is invalid

    Returns:
        T: the value of set field
    """
    if field_name not in self.__class__.__annotations__:
        raise AttributeError(
            f"{field_name} is not a valid attribute of "
            f"{self.__class__.__name__}.")

    setattr(self, field_name, valid(field_value))
    return field_value

step(*args)

Casts anything to a step.

Returns:

Name Type Description
Self Self

these steps

Source code in qa-pytest-commons/src/qa_pytest_commons/generic_steps.py
172
173
174
175
176
177
178
179
180
@final
def step(self, *args: Any) -> Self:
    """
    Casts anything to a step.

    Returns:
        Self: these steps
    """
    return self

tracing(value)

Logs value at DEBUG level using the logger of this steps class.

Parameters:

Name Type Description Default
value Any

The value to log.

required

Returns: Self: these steps

Source code in qa-pytest-commons/src/qa_pytest_commons/generic_steps.py
182
183
184
185
186
187
188
189
190
191
192
193
@final
def tracing(self, value: Any) -> Self:
    """
    Logs value at DEBUG level using the logger of this steps class.

    Args:
        value (Any): The value to log.
    Returns:
        Self: these steps
    """
    self.log.debug(f"=== {value}")
    return self

waiting(duration=timedelta(seconds=0))

Blocks current thread for specified duration.

Parameters:

Name Type Description Default
duration timedelta

How long to wait. Defaults to 0 seconds.

timedelta(seconds=0)

Returns: Self: these steps

Source code in qa-pytest-commons/src/qa_pytest_commons/generic_steps.py
195
196
197
198
199
200
201
202
203
204
205
206
207
@final
@Context.traced
def waiting(self, duration: timedelta = timedelta(seconds=0)) -> Self:
    """
    Blocks current thread for specified duration.

    Args:
        duration (timedelta, optional): How long to wait. Defaults to 0 seconds.
    Returns:
        Self: these steps
    """
    sleep_for(duration)
    return self