pytest-loco 1.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pytest_loco-1.3.0/LICENSE +24 -0
- pytest_loco-1.3.0/PKG-INFO +329 -0
- pytest_loco-1.3.0/README.md +299 -0
- pytest_loco-1.3.0/pyproject.toml +190 -0
- pytest_loco-1.3.0/src/pytest_loco/__init__.py +14 -0
- pytest_loco-1.3.0/src/pytest_loco/__main__.py +144 -0
- pytest_loco-1.3.0/src/pytest_loco/builtins/__init__.py +1 -0
- pytest_loco-1.3.0/src/pytest_loco/builtins/actors.py +35 -0
- pytest_loco-1.3.0/src/pytest_loco/builtins/checkers.py +337 -0
- pytest_loco-1.3.0/src/pytest_loco/builtins/instructions.py +409 -0
- pytest_loco-1.3.0/src/pytest_loco/builtins/lookups.py +181 -0
- pytest_loco-1.3.0/src/pytest_loco/context.py +51 -0
- pytest_loco-1.3.0/src/pytest_loco/core/__init__.py +22 -0
- pytest_loco-1.3.0/src/pytest_loco/core/builder.py +188 -0
- pytest_loco-1.3.0/src/pytest_loco/core/loader.py +269 -0
- pytest_loco-1.3.0/src/pytest_loco/core/parser.py +278 -0
- pytest_loco-1.3.0/src/pytest_loco/errors.py +546 -0
- pytest_loco-1.3.0/src/pytest_loco/extensions/__init__.py +112 -0
- pytest_loco-1.3.0/src/pytest_loco/extensions/actors.py +125 -0
- pytest_loco-1.3.0/src/pytest_loco/extensions/checkers.py +111 -0
- pytest_loco-1.3.0/src/pytest_loco/extensions/contents.py +386 -0
- pytest_loco-1.3.0/src/pytest_loco/extensions/instructions.py +60 -0
- pytest_loco-1.3.0/src/pytest_loco/extensions/parameters.py +224 -0
- pytest_loco-1.3.0/src/pytest_loco/jsonschema.py +107 -0
- pytest_loco-1.3.0/src/pytest_loco/models.py +192 -0
- pytest_loco-1.3.0/src/pytest_loco/names.py +70 -0
- pytest_loco-1.3.0/src/pytest_loco/plugin/__init__.py +110 -0
- pytest_loco-1.3.0/src/pytest_loco/plugin/case.py +340 -0
- pytest_loco-1.3.0/src/pytest_loco/plugin/spec.py +107 -0
- pytest_loco-1.3.0/src/pytest_loco/py.typed +0 -0
- pytest_loco-1.3.0/src/pytest_loco/schema/__init__.py +27 -0
- pytest_loco-1.3.0/src/pytest_loco/schema/actions.py +166 -0
- pytest_loco-1.3.0/src/pytest_loco/schema/cases.py +141 -0
- pytest_loco-1.3.0/src/pytest_loco/schema/checks.py +76 -0
- pytest_loco-1.3.0/src/pytest_loco/schema/contents.py +85 -0
- pytest_loco-1.3.0/src/pytest_loco/schema/contexts.py +72 -0
- pytest_loco-1.3.0/src/pytest_loco/schema/inputs.py +329 -0
- pytest_loco-1.3.0/src/pytest_loco/schema/instructions.py +49 -0
- pytest_loco-1.3.0/src/pytest_loco/values.py +103 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
BSD 2-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025, Mikhalev Oleg and others
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
16
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
17
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
18
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
19
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
20
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
21
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
22
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
23
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
24
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pytest-loco
|
|
3
|
+
Version: 1.3.0
|
|
4
|
+
Summary: Another one YAML-based DSL for testing
|
|
5
|
+
License-Expression: BSD-2-Clause
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Author: Mikhalev Oleg
|
|
8
|
+
Author-email: mhalairt@gmail.com
|
|
9
|
+
Requires-Python: >3.13,<4
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Environment :: Plugins
|
|
12
|
+
Classifier: Framework :: Pytest
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development :: Testing
|
|
20
|
+
Classifier: Topic :: Utilities
|
|
21
|
+
Requires-Dist: click (>=8.3.1,<9.0.0)
|
|
22
|
+
Requires-Dist: pydantic (>=2.12.5,<3.0.0)
|
|
23
|
+
Requires-Dist: pydantic-settings (>=2.12.0,<3.0.0)
|
|
24
|
+
Requires-Dist: pytest (>=9.0.2,<10.0.0)
|
|
25
|
+
Requires-Dist: pyyaml (>=6.0.3,<7.0.0)
|
|
26
|
+
Project-URL: Issues, https://github.com/pytest-loco/pytest-loco/issues
|
|
27
|
+
Project-URL: Source Code, https://github.com/pytest-loco/pytest-loco
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# pytest-loco
|
|
31
|
+
|
|
32
|
+
Declarative DSL for structured, extensible test scenarios in pytest.
|
|
33
|
+
|
|
34
|
+
`pytest-loco` introduces a YAML-based domain-specific language (DSL) for
|
|
35
|
+
describing test workflows in a declarative and composable way.
|
|
36
|
+
It is designed to support structured validation, data-driven execution,
|
|
37
|
+
and pluggable extensions such as HTTP, JSON, and custom domain logic.
|
|
38
|
+
|
|
39
|
+
## Install
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
> pip install pytest-loco
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Requirements:
|
|
46
|
+
- Python 3.13 or higher
|
|
47
|
+
|
|
48
|
+
## Concepts
|
|
49
|
+
|
|
50
|
+
### Definitions
|
|
51
|
+
|
|
52
|
+
A test file is a sequence of YAML documents, each document has a `spec`
|
|
53
|
+
that defines its role.
|
|
54
|
+
|
|
55
|
+
Document represents a `step` by default, but the first document in a file may define:
|
|
56
|
+
|
|
57
|
+
- a `case` (runnable scenario), or
|
|
58
|
+
- a `template` (reusable, non-runnable definition)
|
|
59
|
+
|
|
60
|
+
Subsequent documents define executable steps.
|
|
61
|
+
|
|
62
|
+
#### Case
|
|
63
|
+
|
|
64
|
+
A `case` represents a runnable test scenario.
|
|
65
|
+
|
|
66
|
+
```yaml
|
|
67
|
+
spec: case
|
|
68
|
+
title: Short human-readable case title
|
|
69
|
+
description: >
|
|
70
|
+
Detailed human-readable description of the scenario.
|
|
71
|
+
May span multiple lines and is intended for documentation
|
|
72
|
+
and reporting purposes
|
|
73
|
+
metadata:
|
|
74
|
+
tags:
|
|
75
|
+
- engine
|
|
76
|
+
- example
|
|
77
|
+
vars:
|
|
78
|
+
baseUrl: https://httpbin.org
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Only documents under a `case` are executed.
|
|
82
|
+
|
|
83
|
+
#### Template
|
|
84
|
+
|
|
85
|
+
A `template` defines reusable logic that can be applied to multiple cases.
|
|
86
|
+
|
|
87
|
+
```yaml
|
|
88
|
+
spec: template
|
|
89
|
+
title: Short human-readable template title
|
|
90
|
+
description: >
|
|
91
|
+
Detailed human-readable description of the scenario template.
|
|
92
|
+
May span multiple lines and is intended for documentation
|
|
93
|
+
and reporting purposes
|
|
94
|
+
params:
|
|
95
|
+
- name: baseUrl
|
|
96
|
+
type: string
|
|
97
|
+
default: https://httpbin.org
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Templates allow:
|
|
101
|
+
|
|
102
|
+
- Reuse of common workflows
|
|
103
|
+
- Parameterized execution
|
|
104
|
+
- Elimination of duplication
|
|
105
|
+
- Standardization of expectations
|
|
106
|
+
|
|
107
|
+
Templates are resolved at parse time and merged into the execution graph.
|
|
108
|
+
|
|
109
|
+
```yaml
|
|
110
|
+
...
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
title: Example of invoking the template from a case
|
|
114
|
+
description: >
|
|
115
|
+
This step demonstrates how a template can be invoked from
|
|
116
|
+
within a case using the `include` action. The caller may pass
|
|
117
|
+
variables into the action context via `vars`. These variables
|
|
118
|
+
are treated as parameters of the invoked template. The template
|
|
119
|
+
execution context is isolated: only values explicitly declared
|
|
120
|
+
as template parameters are transferred and shared.
|
|
121
|
+
action: include
|
|
122
|
+
vars:
|
|
123
|
+
argument: OK
|
|
124
|
+
file: echo.yaml
|
|
125
|
+
export:
|
|
126
|
+
value: !var result.value
|
|
127
|
+
expect:
|
|
128
|
+
- title: Value exists in include result
|
|
129
|
+
value: !var value
|
|
130
|
+
match: OK
|
|
131
|
+
|
|
132
|
+
...
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Steps
|
|
136
|
+
|
|
137
|
+
#### Actions
|
|
138
|
+
|
|
139
|
+
Actions are the executable units of a case, each step document
|
|
140
|
+
describes a single action invocation with expectations block.
|
|
141
|
+
|
|
142
|
+
The core itself does not implement domain-specific logic.
|
|
143
|
+
The plugin executes the action implementation. Action resolution
|
|
144
|
+
working with `action` field as a symbolic identifier (for example,
|
|
145
|
+
`http.get`).
|
|
146
|
+
|
|
147
|
+
At runtime:
|
|
148
|
+
- The core locates the plugin that registered this action
|
|
149
|
+
- The action schema is validated
|
|
150
|
+
- Arguments are parsed and compiled (including deferred expressions)
|
|
151
|
+
- The plugin executes the action implementation
|
|
152
|
+
|
|
153
|
+
The complex example with `pytest-loco-json` and `pytest-loco-http` extensions:
|
|
154
|
+
|
|
155
|
+
```yaml
|
|
156
|
+
...
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
title: Test HTTP GET-method
|
|
160
|
+
description: >
|
|
161
|
+
Detailed human-readable description of the action.
|
|
162
|
+
May span multiple lines and is intended for documentation
|
|
163
|
+
and reporting purposes
|
|
164
|
+
action: http.get
|
|
165
|
+
url: !urljoin baseUrl /get
|
|
166
|
+
params: test: 'true'
|
|
167
|
+
headers:
|
|
168
|
+
accept: application/json
|
|
169
|
+
output: response
|
|
170
|
+
export:
|
|
171
|
+
responseJson: !load
|
|
172
|
+
source: !var response.text
|
|
173
|
+
format: json
|
|
174
|
+
expect:
|
|
175
|
+
- title: Status is 200
|
|
176
|
+
value: !var response.status
|
|
177
|
+
match: 200
|
|
178
|
+
- title: Response contains arguments
|
|
179
|
+
value: !var responseJson.args.test
|
|
180
|
+
match: 'true'
|
|
181
|
+
|
|
182
|
+
...
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
After execution, the action produces a result object.
|
|
186
|
+
|
|
187
|
+
By convention, this result is stored in the case context under
|
|
188
|
+
`result`. But user can redefine output variable name by `output` field.
|
|
189
|
+
|
|
190
|
+
```yaml
|
|
191
|
+
...
|
|
192
|
+
|
|
193
|
+
output: response
|
|
194
|
+
value: !var response.status
|
|
195
|
+
|
|
196
|
+
...
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
The structure of result is defined by the plugin.
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
Actions may define an export block:
|
|
203
|
+
|
|
204
|
+
```yaml
|
|
205
|
+
...
|
|
206
|
+
|
|
207
|
+
export:
|
|
208
|
+
token: !var result.token
|
|
209
|
+
...
|
|
210
|
+
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Export:
|
|
214
|
+
- Evaluates expressions after action execution
|
|
215
|
+
- Stores values in case context
|
|
216
|
+
- Makes them available to subsequent steps
|
|
217
|
+
|
|
218
|
+
Exports are explicit data flow. Nothing is implicitly persisted
|
|
219
|
+
across steps except what is exported.
|
|
220
|
+
|
|
221
|
+
#### Expectations
|
|
222
|
+
|
|
223
|
+
Actions may define expectations.
|
|
224
|
+
|
|
225
|
+
```yaml
|
|
226
|
+
...
|
|
227
|
+
|
|
228
|
+
expect:
|
|
229
|
+
- title: Status is 200
|
|
230
|
+
value: !var result.status
|
|
231
|
+
match: 200
|
|
232
|
+
...
|
|
233
|
+
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Expectations:
|
|
237
|
+
- Are evaluated after export
|
|
238
|
+
- Operate on fully resolved values
|
|
239
|
+
- Produce structured assertion results
|
|
240
|
+
- Are reported individually
|
|
241
|
+
|
|
242
|
+
Failure of an expectation fails the step and the case.
|
|
243
|
+
|
|
244
|
+
By default available:
|
|
245
|
+
- `match` (aliased `eq`, `equal`)
|
|
246
|
+
- `not_match` (aliased `notMatch`, `ne`, `notEqual`)
|
|
247
|
+
- `less_than` (aliased `lt`, `lessThan`)
|
|
248
|
+
- `greater_than` (aliased `gt`, `greaterThan`)
|
|
249
|
+
- `less_than_or_equal` (aliased `lte`, `lessThanOrEqual`)
|
|
250
|
+
- `greater_than_or_equal` (aliased `gte`, `greaterThanOrEqual`)
|
|
251
|
+
- `regex` (aliased `reMatch`, `regexMatch`)
|
|
252
|
+
|
|
253
|
+
Expectations can be extended by plugins.
|
|
254
|
+
|
|
255
|
+
### Context
|
|
256
|
+
|
|
257
|
+
The context is the runtime state container for a case.
|
|
258
|
+
|
|
259
|
+
It holds:
|
|
260
|
+
- Case-level variables (defined by `vars` in a case)
|
|
261
|
+
- Case parameters (defined by `params` in a case)
|
|
262
|
+
- Template parameters (defined by `params` in a case)
|
|
263
|
+
- Environment variables (defined by `envs` in a case or a template)
|
|
264
|
+
- Step-level variables (defined by `vars` in a step)
|
|
265
|
+
- Step results (stored in `result` by default)
|
|
266
|
+
- Exported values
|
|
267
|
+
- Deferred expressions
|
|
268
|
+
|
|
269
|
+
Each case has its own isolated context.
|
|
270
|
+
|
|
271
|
+
Instructions such as:
|
|
272
|
+
|
|
273
|
+
```yaml
|
|
274
|
+
...
|
|
275
|
+
|
|
276
|
+
value: !var result.status
|
|
277
|
+
...
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
are compiled at parse time but executed at runtime.
|
|
281
|
+
|
|
282
|
+
Deferred evaluation allows:
|
|
283
|
+
- Accessing results of previous steps
|
|
284
|
+
- Chained transformations
|
|
285
|
+
- Late binding of values
|
|
286
|
+
- Context-aware resolution
|
|
287
|
+
|
|
288
|
+
Evaluation order is deterministic and follows step order.
|
|
289
|
+
|
|
290
|
+
### Errors
|
|
291
|
+
|
|
292
|
+
Parse Errors (before execution begins):
|
|
293
|
+
- Invalid YAML structure
|
|
294
|
+
- Invalid schema
|
|
295
|
+
- Unknown action or expectation
|
|
296
|
+
- Unknown instruction
|
|
297
|
+
- Invalid parameter declarations
|
|
298
|
+
|
|
299
|
+
In this case, the test case does not start execution.
|
|
300
|
+
|
|
301
|
+
Runtime Errors (during execution)
|
|
302
|
+
- Exception raised inside an action or an expectation
|
|
303
|
+
- Deferred evaluation failure
|
|
304
|
+
- Type validation failure
|
|
305
|
+
- Data transformation errors
|
|
306
|
+
|
|
307
|
+
Any failures must be deterministic and predictable, so
|
|
308
|
+
deliberate simplifications of semantics and behavior have
|
|
309
|
+
been introduced into the core:
|
|
310
|
+
- No implicit continuation after failure
|
|
311
|
+
- No silent exception suppression
|
|
312
|
+
- No partially committed state
|
|
313
|
+
- No automatic retries unless implemented by a plugin
|
|
314
|
+
|
|
315
|
+
## VSCode integration
|
|
316
|
+
|
|
317
|
+
Recommended extension:
|
|
318
|
+
[YAML Language Support by Red Hat](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml)
|
|
319
|
+
|
|
320
|
+
DSL schema is available for validation and hints with this extension.
|
|
321
|
+
|
|
322
|
+
Just run:
|
|
323
|
+
|
|
324
|
+
```sh
|
|
325
|
+
> pytest-loco vscode-configure
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
at the root of a project.
|
|
329
|
+
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
# pytest-loco
|
|
2
|
+
|
|
3
|
+
Declarative DSL for structured, extensible test scenarios in pytest.
|
|
4
|
+
|
|
5
|
+
`pytest-loco` introduces a YAML-based domain-specific language (DSL) for
|
|
6
|
+
describing test workflows in a declarative and composable way.
|
|
7
|
+
It is designed to support structured validation, data-driven execution,
|
|
8
|
+
and pluggable extensions such as HTTP, JSON, and custom domain logic.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
> pip install pytest-loco
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Requirements:
|
|
17
|
+
- Python 3.13 or higher
|
|
18
|
+
|
|
19
|
+
## Concepts
|
|
20
|
+
|
|
21
|
+
### Definitions
|
|
22
|
+
|
|
23
|
+
A test file is a sequence of YAML documents, each document has a `spec`
|
|
24
|
+
that defines its role.
|
|
25
|
+
|
|
26
|
+
Document represents a `step` by default, but the first document in a file may define:
|
|
27
|
+
|
|
28
|
+
- a `case` (runnable scenario), or
|
|
29
|
+
- a `template` (reusable, non-runnable definition)
|
|
30
|
+
|
|
31
|
+
Subsequent documents define executable steps.
|
|
32
|
+
|
|
33
|
+
#### Case
|
|
34
|
+
|
|
35
|
+
A `case` represents a runnable test scenario.
|
|
36
|
+
|
|
37
|
+
```yaml
|
|
38
|
+
spec: case
|
|
39
|
+
title: Short human-readable case title
|
|
40
|
+
description: >
|
|
41
|
+
Detailed human-readable description of the scenario.
|
|
42
|
+
May span multiple lines and is intended for documentation
|
|
43
|
+
and reporting purposes
|
|
44
|
+
metadata:
|
|
45
|
+
tags:
|
|
46
|
+
- engine
|
|
47
|
+
- example
|
|
48
|
+
vars:
|
|
49
|
+
baseUrl: https://httpbin.org
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Only documents under a `case` are executed.
|
|
53
|
+
|
|
54
|
+
#### Template
|
|
55
|
+
|
|
56
|
+
A `template` defines reusable logic that can be applied to multiple cases.
|
|
57
|
+
|
|
58
|
+
```yaml
|
|
59
|
+
spec: template
|
|
60
|
+
title: Short human-readable template title
|
|
61
|
+
description: >
|
|
62
|
+
Detailed human-readable description of the scenario template.
|
|
63
|
+
May span multiple lines and is intended for documentation
|
|
64
|
+
and reporting purposes
|
|
65
|
+
params:
|
|
66
|
+
- name: baseUrl
|
|
67
|
+
type: string
|
|
68
|
+
default: https://httpbin.org
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Templates allow:
|
|
72
|
+
|
|
73
|
+
- Reuse of common workflows
|
|
74
|
+
- Parameterized execution
|
|
75
|
+
- Elimination of duplication
|
|
76
|
+
- Standardization of expectations
|
|
77
|
+
|
|
78
|
+
Templates are resolved at parse time and merged into the execution graph.
|
|
79
|
+
|
|
80
|
+
```yaml
|
|
81
|
+
...
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
title: Example of invoking the template from a case
|
|
85
|
+
description: >
|
|
86
|
+
This step demonstrates how a template can be invoked from
|
|
87
|
+
within a case using the `include` action. The caller may pass
|
|
88
|
+
variables into the action context via `vars`. These variables
|
|
89
|
+
are treated as parameters of the invoked template. The template
|
|
90
|
+
execution context is isolated: only values explicitly declared
|
|
91
|
+
as template parameters are transferred and shared.
|
|
92
|
+
action: include
|
|
93
|
+
vars:
|
|
94
|
+
argument: OK
|
|
95
|
+
file: echo.yaml
|
|
96
|
+
export:
|
|
97
|
+
value: !var result.value
|
|
98
|
+
expect:
|
|
99
|
+
- title: Value exists in include result
|
|
100
|
+
value: !var value
|
|
101
|
+
match: OK
|
|
102
|
+
|
|
103
|
+
...
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Steps
|
|
107
|
+
|
|
108
|
+
#### Actions
|
|
109
|
+
|
|
110
|
+
Actions are the executable units of a case, each step document
|
|
111
|
+
describes a single action invocation with expectations block.
|
|
112
|
+
|
|
113
|
+
The core itself does not implement domain-specific logic.
|
|
114
|
+
The plugin executes the action implementation. Action resolution
|
|
115
|
+
working with `action` field as a symbolic identifier (for example,
|
|
116
|
+
`http.get`).
|
|
117
|
+
|
|
118
|
+
At runtime:
|
|
119
|
+
- The core locates the plugin that registered this action
|
|
120
|
+
- The action schema is validated
|
|
121
|
+
- Arguments are parsed and compiled (including deferred expressions)
|
|
122
|
+
- The plugin executes the action implementation
|
|
123
|
+
|
|
124
|
+
The complex example with `pytest-loco-json` and `pytest-loco-http` extensions:
|
|
125
|
+
|
|
126
|
+
```yaml
|
|
127
|
+
...
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
title: Test HTTP GET-method
|
|
131
|
+
description: >
|
|
132
|
+
Detailed human-readable description of the action.
|
|
133
|
+
May span multiple lines and is intended for documentation
|
|
134
|
+
and reporting purposes
|
|
135
|
+
action: http.get
|
|
136
|
+
url: !urljoin baseUrl /get
|
|
137
|
+
params: test: 'true'
|
|
138
|
+
headers:
|
|
139
|
+
accept: application/json
|
|
140
|
+
output: response
|
|
141
|
+
export:
|
|
142
|
+
responseJson: !load
|
|
143
|
+
source: !var response.text
|
|
144
|
+
format: json
|
|
145
|
+
expect:
|
|
146
|
+
- title: Status is 200
|
|
147
|
+
value: !var response.status
|
|
148
|
+
match: 200
|
|
149
|
+
- title: Response contains arguments
|
|
150
|
+
value: !var responseJson.args.test
|
|
151
|
+
match: 'true'
|
|
152
|
+
|
|
153
|
+
...
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
After execution, the action produces a result object.
|
|
157
|
+
|
|
158
|
+
By convention, this result is stored in the case context under
|
|
159
|
+
`result`. But user can redefine output variable name by `output` field.
|
|
160
|
+
|
|
161
|
+
```yaml
|
|
162
|
+
...
|
|
163
|
+
|
|
164
|
+
output: response
|
|
165
|
+
value: !var response.status
|
|
166
|
+
|
|
167
|
+
...
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
The structure of result is defined by the plugin.
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
Actions may define an export block:
|
|
174
|
+
|
|
175
|
+
```yaml
|
|
176
|
+
...
|
|
177
|
+
|
|
178
|
+
export:
|
|
179
|
+
token: !var result.token
|
|
180
|
+
...
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Export:
|
|
185
|
+
- Evaluates expressions after action execution
|
|
186
|
+
- Stores values in case context
|
|
187
|
+
- Makes them available to subsequent steps
|
|
188
|
+
|
|
189
|
+
Exports are explicit data flow. Nothing is implicitly persisted
|
|
190
|
+
across steps except what is exported.
|
|
191
|
+
|
|
192
|
+
#### Expectations
|
|
193
|
+
|
|
194
|
+
Actions may define expectations.
|
|
195
|
+
|
|
196
|
+
```yaml
|
|
197
|
+
...
|
|
198
|
+
|
|
199
|
+
expect:
|
|
200
|
+
- title: Status is 200
|
|
201
|
+
value: !var result.status
|
|
202
|
+
match: 200
|
|
203
|
+
...
|
|
204
|
+
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Expectations:
|
|
208
|
+
- Are evaluated after export
|
|
209
|
+
- Operate on fully resolved values
|
|
210
|
+
- Produce structured assertion results
|
|
211
|
+
- Are reported individually
|
|
212
|
+
|
|
213
|
+
Failure of an expectation fails the step and the case.
|
|
214
|
+
|
|
215
|
+
By default available:
|
|
216
|
+
- `match` (aliased `eq`, `equal`)
|
|
217
|
+
- `not_match` (aliased `notMatch`, `ne`, `notEqual`)
|
|
218
|
+
- `less_than` (aliased `lt`, `lessThan`)
|
|
219
|
+
- `greater_than` (aliased `gt`, `greaterThan`)
|
|
220
|
+
- `less_than_or_equal` (aliased `lte`, `lessThanOrEqual`)
|
|
221
|
+
- `greater_than_or_equal` (aliased `gte`, `greaterThanOrEqual`)
|
|
222
|
+
- `regex` (aliased `reMatch`, `regexMatch`)
|
|
223
|
+
|
|
224
|
+
Expectations can be extended by plugins.
|
|
225
|
+
|
|
226
|
+
### Context
|
|
227
|
+
|
|
228
|
+
The context is the runtime state container for a case.
|
|
229
|
+
|
|
230
|
+
It holds:
|
|
231
|
+
- Case-level variables (defined by `vars` in a case)
|
|
232
|
+
- Case parameters (defined by `params` in a case)
|
|
233
|
+
- Template parameters (defined by `params` in a case)
|
|
234
|
+
- Environment variables (defined by `envs` in a case or a template)
|
|
235
|
+
- Step-level variables (defined by `vars` in a step)
|
|
236
|
+
- Step results (stored in `result` by default)
|
|
237
|
+
- Exported values
|
|
238
|
+
- Deferred expressions
|
|
239
|
+
|
|
240
|
+
Each case has its own isolated context.
|
|
241
|
+
|
|
242
|
+
Instructions such as:
|
|
243
|
+
|
|
244
|
+
```yaml
|
|
245
|
+
...
|
|
246
|
+
|
|
247
|
+
value: !var result.status
|
|
248
|
+
...
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
are compiled at parse time but executed at runtime.
|
|
252
|
+
|
|
253
|
+
Deferred evaluation allows:
|
|
254
|
+
- Accessing results of previous steps
|
|
255
|
+
- Chained transformations
|
|
256
|
+
- Late binding of values
|
|
257
|
+
- Context-aware resolution
|
|
258
|
+
|
|
259
|
+
Evaluation order is deterministic and follows step order.
|
|
260
|
+
|
|
261
|
+
### Errors
|
|
262
|
+
|
|
263
|
+
Parse Errors (before execution begins):
|
|
264
|
+
- Invalid YAML structure
|
|
265
|
+
- Invalid schema
|
|
266
|
+
- Unknown action or expectation
|
|
267
|
+
- Unknown instruction
|
|
268
|
+
- Invalid parameter declarations
|
|
269
|
+
|
|
270
|
+
In this case, the test case does not start execution.
|
|
271
|
+
|
|
272
|
+
Runtime Errors (during execution)
|
|
273
|
+
- Exception raised inside an action or an expectation
|
|
274
|
+
- Deferred evaluation failure
|
|
275
|
+
- Type validation failure
|
|
276
|
+
- Data transformation errors
|
|
277
|
+
|
|
278
|
+
Any failures must be deterministic and predictable, so
|
|
279
|
+
deliberate simplifications of semantics and behavior have
|
|
280
|
+
been introduced into the core:
|
|
281
|
+
- No implicit continuation after failure
|
|
282
|
+
- No silent exception suppression
|
|
283
|
+
- No partially committed state
|
|
284
|
+
- No automatic retries unless implemented by a plugin
|
|
285
|
+
|
|
286
|
+
## VSCode integration
|
|
287
|
+
|
|
288
|
+
Recommended extension:
|
|
289
|
+
[YAML Language Support by Red Hat](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml)
|
|
290
|
+
|
|
291
|
+
DSL schema is available for validation and hints with this extension.
|
|
292
|
+
|
|
293
|
+
Just run:
|
|
294
|
+
|
|
295
|
+
```sh
|
|
296
|
+
> pytest-loco vscode-configure
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
at the root of a project.
|