pytest-dsl 0.6.0__tar.gz → 0.8.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.
Files changed (85) hide show
  1. pytest_dsl-0.8.0/PKG-INFO +1088 -0
  2. pytest_dsl-0.8.0/README.md +1057 -0
  3. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pyproject.toml +1 -1
  4. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/core/dsl_executor.py +96 -15
  5. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/core/lexer.py +21 -5
  6. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/core/parser.py +68 -5
  7. pytest_dsl-0.8.0/pytest_dsl/core/parsetab.py +130 -0
  8. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/core/utils.py +43 -23
  9. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/core/variable_utils.py +215 -70
  10. pytest_dsl-0.8.0/pytest_dsl.egg-info/PKG-INFO +1088 -0
  11. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl.egg-info/SOURCES.txt +1 -1
  12. pytest_dsl-0.8.0/tests/test_enhanced_variable_access.py +164 -0
  13. pytest_dsl-0.6.0/PKG-INFO +0 -725
  14. pytest_dsl-0.6.0/README.md +0 -694
  15. pytest_dsl-0.6.0/pytest_dsl/core/parsetab.py +0 -114
  16. pytest_dsl-0.6.0/pytest_dsl/parsetab.py +0 -69
  17. pytest_dsl-0.6.0/pytest_dsl.egg-info/PKG-INFO +0 -725
  18. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/LICENSE +0 -0
  19. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/MANIFEST.in +0 -0
  20. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/__init__.py +0 -0
  21. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/cli.py +0 -0
  22. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/conftest_adapter.py +0 -0
  23. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/core/__init__.py +0 -0
  24. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/core/auth_provider.py +0 -0
  25. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/core/auto_decorator.py +0 -0
  26. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/core/auto_directory.py +0 -0
  27. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/core/context.py +0 -0
  28. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/core/custom_keyword_manager.py +0 -0
  29. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/core/dsl_executor_utils.py +0 -0
  30. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/core/global_context.py +0 -0
  31. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/core/http_client.py +0 -0
  32. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/core/http_request.py +0 -0
  33. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/core/keyword_manager.py +0 -0
  34. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/core/plugin_discovery.py +0 -0
  35. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/core/yaml_loader.py +0 -0
  36. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/core/yaml_vars.py +0 -0
  37. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/docs/custom_keywords.md +0 -0
  38. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/examples/__init__.py +0 -0
  39. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/examples/assert/assertion_example.auto +0 -0
  40. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/examples/assert/boolean_test.auto +0 -0
  41. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/examples/assert/expression_test.auto +0 -0
  42. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/examples/custom/test_advanced_keywords.auto +0 -0
  43. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/examples/custom/test_custom_keywords.auto +0 -0
  44. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/examples/custom/test_default_values.auto +0 -0
  45. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/examples/http/__init__.py +0 -0
  46. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/examples/http/builtin_auth_test.auto +0 -0
  47. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/examples/http/file_reference_test.auto +0 -0
  48. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/examples/http/http_advanced.auto +0 -0
  49. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/examples/http/http_example.auto +0 -0
  50. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/examples/http/http_length_test.auto +0 -0
  51. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/examples/http/http_retry_assertions.auto +0 -0
  52. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/examples/http/http_retry_assertions_enhanced.auto +0 -0
  53. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/examples/http/http_with_yaml.auto +0 -0
  54. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/examples/http/new_retry_test.auto +0 -0
  55. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/examples/http/retry_assertions_only.auto +0 -0
  56. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/examples/http/retry_config_only.auto +0 -0
  57. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/examples/http/retry_debug.auto +0 -0
  58. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/examples/http/retry_with_fix.auto +0 -0
  59. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/examples/http/simple_retry.auto +0 -0
  60. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/examples/http/vars.yaml +0 -0
  61. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/examples/quickstart/api_basics.auto +0 -0
  62. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/examples/quickstart/assertions.auto +0 -0
  63. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/examples/quickstart/loops.auto +0 -0
  64. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/examples/test_assert.py +0 -0
  65. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/examples/test_custom_keyword.py +0 -0
  66. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/examples/test_http.py +0 -0
  67. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/examples/test_quickstart.py +0 -0
  68. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/keywords/__init__.py +0 -0
  69. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/keywords/assertion_keywords.py +0 -0
  70. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/keywords/global_keywords.py +0 -0
  71. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/keywords/http_keywords.py +0 -0
  72. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/keywords/system_keywords.py +0 -0
  73. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/main_adapter.py +0 -0
  74. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl/plugin.py +0 -0
  75. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl.egg-info/dependency_links.txt +0 -0
  76. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl.egg-info/entry_points.txt +0 -0
  77. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl.egg-info/requires.txt +0 -0
  78. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/pytest_dsl.egg-info/top_level.txt +0 -0
  79. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/setup.cfg +0 -0
  80. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/setup.py +0 -0
  81. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/tests/test_end_to_end_seamless.py +0 -0
  82. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/tests/test_http_assertions_extractors.py +0 -0
  83. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/tests/test_seamless_variable_sync.py +0 -0
  84. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/tests/test_variable_sync.py +0 -0
  85. {pytest_dsl-0.6.0 → pytest_dsl-0.8.0}/tests/test_variable_sync_demo.py +0 -0
@@ -0,0 +1,1088 @@
1
+ Metadata-Version: 2.4
2
+ Name: pytest-dsl
3
+ Version: 0.8.0
4
+ Summary: A DSL testing framework based on pytest
5
+ Author: Chen Shuanglin
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/felix-1991/pytest-dsl
8
+ Project-URL: Bug Tracker, https://github.com/felix-1991/pytest-dsl/issues
9
+ Classifier: Framework :: Pytest
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Requires-Python: >=3.9
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: pytest>=7.0.0
21
+ Requires-Dist: allure-pytest>=2.9.0
22
+ Requires-Dist: ply>=3.11
23
+ Requires-Dist: filelock>=3.17.0
24
+ Requires-Dist: PyYAML==6.0.2
25
+ Requires-Dist: jsonpath-ng>=1.5.0
26
+ Requires-Dist: requests>=2.28.0
27
+ Requires-Dist: lxml>=4.9.0
28
+ Requires-Dist: jsonschema>=4.17.0
29
+ Requires-Dist: pytz>=2023.3
30
+ Dynamic: license-file
31
+
32
+ # pytest-dsl: 强大的关键字驱动测试自动化框架
33
+
34
+ [![Python Version](https://img.shields.io/badge/python-3.8%2B-blue.svg)](https://python.org)
35
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
36
+ [![PyPI Version](https://img.shields.io/pypi/v/pytest-dsl.svg)](https://pypi.org/project/pytest-dsl/)
37
+
38
+ > 🚀 **让测试自动化变得简单直观** - 使用自然语言风格的DSL编写测试,无需复杂编程技能
39
+
40
+ pytest-dsl是一个革命性的关键字驱动测试框架,基于pytest构建,通过自定义的领域特定语言(DSL)让测试编写变得像写文档一样简单。无论是API测试、UI测试还是其他自动化场景,都能轻松应对。
41
+
42
+ ## ✨ 核心特性
43
+
44
+ - 🎯 **门槛上手低** - 自然语言风格,只需少量编程基础
45
+ - 🔧 **高度可扩展** - 轻松创建自定义关键字
46
+ - 🌐 **分布式执行** - 支持远程关键字调用
47
+ - 🔄 **无缝集成** - 完美兼容pytest生态
48
+ - 📊 **丰富报告** - 集成Allure测试报告
49
+ - 🛡️ **企业级** - 支持变量管理、环境隔离
50
+
51
+ ## 🚀 5分钟快速开始
52
+
53
+ ### 第一步:安装
54
+
55
+ ```bash
56
+ # 使用 pip 安装
57
+ pip install pytest-dsl
58
+
59
+ # 或使用 uv 安装(推荐)
60
+ uv pip install pytest-dsl
61
+ ```
62
+
63
+ ### 第二步:创建第一个测试
64
+
65
+ 创建文件 `hello.dsl`:
66
+
67
+ ```python
68
+ @name: "我的第一个测试"
69
+ @description: "学习pytest-dsl的第一步"
70
+
71
+ # 定义变量
72
+ message = "Hello, pytest-dsl!"
73
+ count = 3
74
+
75
+ # 打印欢迎消息
76
+ [打印], 内容: ${message}
77
+
78
+ # 简单循环
79
+ for i in range(1, ${count} + 1) do
80
+ [打印], 内容: "第 ${i} 次循环"
81
+ end
82
+
83
+ # 测试断言
84
+ [断言], 条件: "${count} == 3", 消息: "计数器应该等于3"
85
+
86
+ teardown do
87
+ [打印], 内容: "测试完成!"
88
+ end
89
+ ```
90
+
91
+ ### 第三步:运行测试
92
+
93
+ ```bash
94
+ # 直接运行DSL文件
95
+ pytest-dsl hello.dsl
96
+
97
+ # 运行目录下所有DSL文件
98
+ pytest-dsl tests/
99
+ ```
100
+
101
+ 🎉 **恭喜!** 您已经成功运行了第一个pytest-dsl测试!
102
+
103
+ ## 📚 基础教程
104
+
105
+ ### 1. 基本语法入门
106
+
107
+ #### 变量和数据类型
108
+
109
+ ```python
110
+ # 字符串变量
111
+ name = "pytest-dsl"
112
+ version = "1.0.0"
113
+
114
+ # 数字变量
115
+ port = 8080
116
+
117
+ # 布尔值变量
118
+ is_enabled = True
119
+ is_disabled = False
120
+
121
+ # 列表
122
+ users = ["alice", "bob", "charlie"]
123
+
124
+ # 字典
125
+ user_info = {"name": "张三", "age": 30, "city": "北京"}
126
+
127
+ # 嵌套字典
128
+ config = {
129
+ "database": {
130
+ "host": "localhost",
131
+ "port": 3306,
132
+ "name": "test_db"
133
+ },
134
+ "api": {
135
+ "base_url": "https://api.example.com",
136
+ "timeout": 30
137
+ }
138
+ }
139
+
140
+ # 访问字典值
141
+ username = ${user_info["name"]}
142
+ db_host = ${config["database"]["host"]}
143
+ ```
144
+
145
+ #### 流程控制
146
+
147
+ ```python
148
+ # 条件判断
149
+ status = "success"
150
+ if status == "success" do
151
+ [打印], 内容: "测试通过"
152
+ else
153
+ [打印], 内容: "测试失败"
154
+ end
155
+
156
+ # 使用布尔值的条件判断
157
+ is_ready = True
158
+ if ${is_ready} do
159
+ [打印], 内容: "系统就绪"
160
+ end
161
+
162
+ # 循环结构
163
+ num = 4
164
+ for i in range(1, num) do
165
+ [打印], 内容: "执行第 ${i} 次"
166
+ end
167
+
168
+ # 循环中的break和continue
169
+ for j in range(1, 11) do
170
+ # 跳过偶数
171
+ if ${j} % 2 == 0 do
172
+ continue
173
+ end
174
+
175
+ # 当达到7时退出循环
176
+ if ${j} == 7 do
177
+ [打印], 内容: "达到7,退出循环"
178
+ break
179
+ end
180
+
181
+ [打印], 内容: "奇数: ${j}"
182
+ end
183
+ ```
184
+
185
+ ### 2. 内置关键字详解
186
+
187
+ #### 基础关键字
188
+
189
+ ```python
190
+ # 打印输出
191
+ [打印], 内容: "Hello World"
192
+
193
+ # 断言测试
194
+ [断言], 条件: "1 + 1 == 2", 消息: "数学计算错误"
195
+
196
+ # 等待
197
+ [等待], 秒数: 2
198
+
199
+ # 生成随机数
200
+ random_num = [生成随机数], 最小值: 1, 最大值: 100
201
+ [打印], 内容: "随机数: ${random_num}"
202
+ ```
203
+
204
+ #### 变量操作
205
+
206
+ ```python
207
+ [设置全局变量], 变量名: "test_env", 值: "development"
208
+
209
+ # 获取全局变量
210
+ env = [获取全局变量], 变量名: "test_env"
211
+ [打印], 内容: "当前环境: ${env}"
212
+ ```
213
+
214
+ ### 3. 自定义关键字(函数)
215
+
216
+ 自定义关键字让您可以封装复用的测试逻辑:
217
+
218
+ ```python
219
+ @name: "自定义关键字示例"
220
+
221
+ # 定义一个计算器关键字
222
+ function 计算器 (操作, 数字1, 数字2=0) do
223
+ if ${操作} == "加法" do
224
+ [打印],内容: "执行加法操作"
225
+ 结果 = ${数字1} + ${数字2}
226
+ else
227
+ 结果 = 12
228
+ end
229
+
230
+ [打印], 内容: "${数字1} ${操作} ${数字2} = ${结果}"
231
+ return ${结果}
232
+ end
233
+
234
+ # 使用自定义关键字
235
+ sum_result = [计算器], 操作: "加法", 数字1: 10, 数字2: 5
236
+ product_result = [计算器], 操作: "其他", 数字1: 3, 数字2: 4
237
+
238
+ # 验证结果
239
+ [断言], 条件: "${sum_result} == 15", 消息: "加法计算错误"
240
+ [断言], 条件: "${product_result} == 12", 消息: "其他计算错误"
241
+ ```
242
+
243
+ #### 资源文件复用
244
+
245
+ 将常用关键字保存在资源文件中(`.resource`),实现跨项目复用:
246
+
247
+ **创建资源文件 `utils.resource`:**
248
+
249
+ ```python
250
+ @name: "通用工具关键字"
251
+
252
+ # 定义一个简单的关键字(函数)
253
+ function 拼接字符串 (前缀, 后缀="默认后缀") do
254
+ # 直接使用关键字参数
255
+ [打印],内容: "拼接前缀: ${前缀} 和后缀: ${后缀}"
256
+
257
+ # 保存到变量中
258
+ 结果变量 = "${前缀}${后缀}"
259
+ [打印],内容: "拼接结果: ${结果变量}"
260
+
261
+ # 返回结果
262
+ return ${结果变量}
263
+ end
264
+ ```
265
+
266
+ **在测试中使用资源文件:**
267
+
268
+ ```python
269
+ @name: "使用资源文件示例"
270
+ @import: "utils.resource"
271
+
272
+ # 使用自定义关键字
273
+ 问候语 = [拼接字符串],前缀: "你好, ",后缀: "世界"
274
+
275
+ # 只传递必要参数,使用默认值
276
+ 简单问候 = [拼接字符串],前缀: "你好"
277
+ [打印],内容: ${简单问候} # 输出: 你好默认后缀
278
+ ```
279
+
280
+ ### 4. API测试入门
281
+
282
+ #### 简单的GET请求
283
+
284
+ ```python
285
+ @name: "API测试入门"
286
+ @description: "学习基本的API测试方法"
287
+
288
+ # 简单的GET请求
289
+ [HTTP请求], 客户端: "default", 配置: '''
290
+ method: GET
291
+ url: https://jsonplaceholder.typicode.com/posts/1
292
+ asserts:
293
+ - ["status", "eq", 200]
294
+ - ["jsonpath", "$.title", "contains", "sunt"]
295
+ ''', 步骤名称: "获取文章详情"
296
+ ```
297
+
298
+ #### 带参数的请求
299
+
300
+ ```python
301
+ # 带查询参数的GET请求
302
+ [HTTP请求], 客户端: "default", 配置: '''
303
+ method: GET
304
+ url: https://jsonplaceholder.typicode.com/posts
305
+ request:
306
+ params:
307
+ userId: 1
308
+ _limit: 5
309
+ asserts:
310
+ - ["status", "eq", 200]
311
+ - ["jsonpath", "$", "length", "eq", 5]
312
+ ''', 步骤名称: "获取用户文章列表"
313
+ ```
314
+
315
+ #### 数据捕获和变量使用
316
+
317
+ ```python
318
+ # 捕获响应数据
319
+ [HTTP请求], 客户端: "default", 配置: '''
320
+ method: GET
321
+ url: https://jsonplaceholder.typicode.com/users/1
322
+ captures:
323
+ user_name: ["jsonpath", "$.name"]
324
+ user_email: ["jsonpath", "$.email"]
325
+ asserts:
326
+ - ["status", "eq", 200]
327
+ ''', 步骤名称: "获取用户信息"
328
+
329
+ # 使用捕获的变量
330
+ [打印], 内容: "用户名: ${user_name}"
331
+ [打印], 内容: "邮箱: ${user_email}"
332
+
333
+ # 在后续请求中使用
334
+ [HTTP请求], 客户端: "default", 配置: '''
335
+ method: GET
336
+ url: https://jsonplaceholder.typicode.com/posts
337
+ request:
338
+ params:
339
+ userId: 1
340
+ asserts:
341
+ - ["status", "eq", 200]
342
+ ''', 步骤名称: "根据用户ID获取文章"
343
+ ```
344
+
345
+ ## 🚀 进阶功能
346
+
347
+ ### 1. 环境配置管理
348
+
349
+ #### YAML变量文件
350
+
351
+ 创建 `config/dev.yaml` 管理开发环境配置:
352
+
353
+ ```yaml
354
+ # 环境配置
355
+ environment: "development"
356
+ debug: true
357
+
358
+ # API配置
359
+ api:
360
+ base_url: "https://jsonplaceholder.typicode.com"
361
+ timeout: 30
362
+ retry_count: 3
363
+
364
+ # HTTP客户端配置
365
+ http_clients:
366
+ default:
367
+ base_url: "${api.base_url}"
368
+ timeout: ${api.timeout}
369
+ headers:
370
+ Content-Type: "application/json"
371
+ User-Agent: "pytest-dsl/1.0"
372
+
373
+ # 测试数据
374
+ test_users:
375
+ admin:
376
+ username: "admin"
377
+ password: "admin123"
378
+ normal:
379
+ username: "user"
380
+ password: "user123"
381
+
382
+ # 数据库配置
383
+ database:
384
+ host: "localhost"
385
+ port: 5432
386
+ name: "test_db"
387
+ ```
388
+
389
+ #### 使用配置文件
390
+
391
+ ```bash
392
+ # 运行时指定配置文件
393
+ pytest-dsl tests/ --yaml-vars config/dev.yaml
394
+ ```
395
+
396
+ #### 在DSL中使用配置
397
+
398
+ ```python
399
+ @name: "使用环境配置"
400
+
401
+ # 直接使用YAML中的变量
402
+ [打印], 内容: "当前环境: ${environment}"
403
+ [打印], 内容: "API地址: ${api.base_url}"
404
+
405
+ # 使用嵌套配置(支持增强的变量访问语法)
406
+ admin_user = ${test_users.admin.username}
407
+ admin_pass = ${test_users.admin.password}
408
+
409
+ # 支持数组索引访问
410
+ first_user = ${users_array[0].name}
411
+ last_user = ${users_array[-1].name}
412
+
413
+ # 支持字典键访问
414
+ api_server = ${config_map["api-server"]}
415
+ timeout_config = ${config_map['timeout']}
416
+
417
+ [HTTP请求], 客户端: "default", 配置: '''
418
+ method: POST
419
+ url: ${api.base_url}/auth/login
420
+ request:
421
+ json:
422
+ username: "${admin_user}"
423
+ password: "${admin_pass}"
424
+ asserts:
425
+ - ["status", "eq", 200]
426
+ ''', 步骤名称: "管理员登录"
427
+ ```
428
+
429
+ ### 2. 增强的变量访问语法
430
+
431
+ pytest-dsl 支持类似 Python 的强大变量访问语法:
432
+
433
+ #### 支持的语法类型
434
+
435
+ ```python
436
+ # 基本变量访问
437
+ ${variable_name}
438
+
439
+ # 点号访问(对象属性)
440
+ ${object.property}
441
+ ${nested.object.property}
442
+
443
+ # 数组索引访问
444
+ ${array[0]} # 第一个元素
445
+ ${array[-1]} # 最后一个元素
446
+
447
+ # 字典键访问
448
+ ${dict["key"]} # 使用双引号
449
+ ${dict['key']} # 使用单引号
450
+
451
+ # 混合访问模式
452
+ ${users[0].name} # 数组中对象的属性
453
+ ${data["users"][0]["name"]} # 嵌套字典和数组
454
+ ${config.servers[0].endpoints["api"]} # 复杂嵌套结构
455
+ ```
456
+
457
+ #### 实际使用示例
458
+
459
+ ```yaml
460
+ # YAML配置文件
461
+ users:
462
+ - id: 1
463
+ name: "张三"
464
+ roles: ["admin", "user"]
465
+ profile:
466
+ email: "zhangsan@example.com"
467
+ settings:
468
+ theme: "dark"
469
+
470
+ config:
471
+ "api-server": "https://api.example.com"
472
+ "timeout": 30
473
+ ```
474
+
475
+ ```python
476
+ # DSL测试文件
477
+ @name: "变量访问语法示例"
478
+
479
+ # 数组访问
480
+ first_user = ${users[0].name}
481
+ [打印], 内容: "第一个用户: ${first_user}"
482
+
483
+ # 嵌套访问
484
+ user_theme = ${users[0].profile.settings.theme}
485
+ [打印], 内容: "用户主题: ${user_theme}"
486
+
487
+ # 字典键访问
488
+ api_server = ${config["api-server"]}
489
+ [打印], 内容: "API服务器: ${api_server}"
490
+
491
+ # 在字符串中使用
492
+ [打印], 内容: "用户${users[0].name}的角色是${users[0].roles[0]}"
493
+ ```
494
+
495
+ 详细文档请参考:[增强的变量访问语法](docs/enhanced_variable_access.md)
496
+
497
+ ### 3. 数据驱动测试
498
+
499
+ #### CSV数据驱动
500
+
501
+ 注意:这种数据驱动模式只有用pytest命令运行的时候才可以
502
+
503
+ 创建 `test_data.csv`:
504
+
505
+ ```csv
506
+ username,password,expected_status,test_case
507
+ admin,admin123,200,管理员登录成功
508
+ user,user123,200,普通用户登录成功
509
+ invalid,wrong,401,错误密码登录失败
510
+ "",admin123,400,空用户名登录失败
511
+ ```
512
+
513
+ 使用CSV数据:
514
+
515
+ ```python
516
+ @name: "登录功能测试"
517
+ @data: "test_data.csv" using csv
518
+ @description: "使用CSV数据测试登录功能"
519
+
520
+ # CSV中的每一行都会执行一次这个测试
521
+ [打印], 内容: "测试用例: ${test_case}"
522
+ [打印], 内容: "用户名: ${username}, 密码: ${password}, 期望状态: ${expected_status}"
523
+
524
+ # 模拟HTTP请求(实际应该是真实的API调用)
525
+ [打印], 内容: "模拟登录请求..."
526
+
527
+ # 简单的条件判断来模拟不同的测试结果
528
+ if "${username}" == "admin" do
529
+ [打印], 内容: "管理员登录测试"
530
+ else
531
+ [打印], 内容: "无效用户登录测试"
532
+ end
533
+
534
+ [打印], 内容: "测试用例: ${test_case} - 完成"
535
+ ```
536
+
537
+ ### 3. 断言重试机制
538
+
539
+ 对于异步API或需要等待的场景:
540
+
541
+ ```python
542
+ # 带重试的断言
543
+ [HTTP请求], 客户端: "default", 配置: '''
544
+ method: GET
545
+ url: https://jsonplaceholder.typicode.com/posts/1
546
+ asserts:
547
+ - ["status", "eq", 200]
548
+ - ["jsonpath", "$.id", "eq", 1]
549
+ ''', 断言重试次数: 3, 断言重试间隔: 1, 步骤名称: "测试断言重试机制"
550
+ ```
551
+
552
+ ### 4. 远程关键字功能
553
+
554
+ pytest-dsl支持分布式测试,可以在不同机器上执行关键字:
555
+
556
+ #### 启动远程服务
557
+
558
+ ```bash
559
+ # 在远程机器上启动关键字服务
560
+ pytest-dsl-server --host 0.0.0.0 --port 8270
561
+
562
+ # 带API密钥的安全启动
563
+ pytest-dsl-server --host 0.0.0.0 --port 8270 --api-key your_secret_key
564
+ ```
565
+
566
+ #### 使用远程关键字
567
+
568
+ **方式一:DSL中直接连接**
569
+
570
+ ```python
571
+ @name: "远程关键字测试"
572
+ @remote: "http://remote-server:8270/" as remote_machine
573
+
574
+ # 在远程机器上执行关键字
575
+ remote_machine|[打印], 内容: "这在远程机器上执行"
576
+ result = remote_machine|[生成随机数], 最小值: 1, 最大值: 100
577
+ [打印], 内容: "远程生成的随机数: ${result}"
578
+ ```
579
+
580
+ **方式二:YAML配置自动加载(推荐)**
581
+
582
+ 在 `config/vars.yaml` 中配置:
583
+
584
+ ```yaml
585
+ remote_servers:
586
+ main_server:
587
+ url: "http://server1:8270/"
588
+ alias: "server1"
589
+ api_key: "your_api_key"
590
+ sync_config:
591
+ sync_global_vars: true
592
+ sync_yaml_vars: true
593
+
594
+ backup_server:
595
+ url: "http://server2:8270/"
596
+ alias: "server2"
597
+ ```
598
+
599
+ 然后直接使用:
600
+
601
+ ```python
602
+ # 无需@remote导入,直接使用
603
+ server1|[HTTP请求], 客户端: "default", 配置: '''
604
+ method: GET
605
+ url: https://jsonplaceholder.typicode.com/posts/1
606
+ '''
607
+
608
+ server2|[打印], 内容: "备用服务器执行"
609
+ ```
610
+
611
+ #### 无缝变量传递
612
+
613
+ 客户端的变量会自动传递到远程服务器:
614
+
615
+ ```python
616
+ # 客户端定义的变量
617
+ api_url = "https://jsonplaceholder.typicode.com"
618
+ user_token = "abc123"
619
+
620
+ # 远程服务器可以直接使用这些变量
621
+ remote_machine|[HTTP请求], 客户端: "default", 配置: '''
622
+ method: GET
623
+ url: ${api_url}/users/1
624
+ request:
625
+ headers:
626
+ Authorization: "Bearer ${user_token}"
627
+ '''
628
+ ```
629
+
630
+ ## 📋 实战案例
631
+
632
+ ### 完整的API测试项目
633
+
634
+ 让我们创建一个完整的API测试项目来演示pytest-dsl的强大功能:
635
+
636
+ #### 项目结构
637
+
638
+ ```
639
+ my-api-tests/
640
+ ├── config/
641
+ │ ├── dev.yaml # 开发环境配置
642
+ │ ├── prod.yaml # 生产环境配置
643
+ │ └── base.yaml # 基础配置
644
+ ├── resources/
645
+ │ ├── auth.resource # 认证相关关键字
646
+ │ └── utils.resource # 工具关键字
647
+ ├── tests/
648
+ │ ├── auth/
649
+ │ │ ├── login.dsl # 登录测试
650
+ │ │ └── logout.dsl # 登出测试
651
+ │ ├── users/
652
+ │ │ ├── create_user.dsl
653
+ │ │ └── get_user.dsl
654
+ │ └── data/
655
+ │ └── users.csv # 测试数据
656
+ ├── test_runner.py # pytest集成
657
+ └── pytest.ini # pytest配置
658
+ ```
659
+
660
+ #### 基础配置 `config/base.yaml`
661
+
662
+ ```yaml
663
+ # 通用配置
664
+ app_name: "My API"
665
+ version: "1.0.0"
666
+
667
+ # HTTP客户端配置
668
+ http_clients:
669
+ default:
670
+ timeout: 30
671
+ headers:
672
+ Content-Type: "application/json"
673
+ User-Agent: "${app_name}/${version}"
674
+ ```
675
+
676
+ #### 开发环境配置 `config/dev.yaml`
677
+
678
+ ```yaml
679
+ # 继承基础配置
680
+ extends: "base.yaml"
681
+
682
+ # 开发环境特定配置
683
+ environment: "development"
684
+ debug: true
685
+
686
+ api:
687
+ base_url: "https://jsonplaceholder.typicode.com"
688
+
689
+ # 测试用户
690
+ test_users:
691
+ admin:
692
+ username: "admin"
693
+ password: "admin123"
694
+ normal:
695
+ username: "testuser"
696
+ password: "test123"
697
+
698
+ # 数据库配置
699
+ database:
700
+ host: "localhost"
701
+ port: 5432
702
+ name: "test_db"
703
+ ```
704
+
705
+ #### 认证关键字 `resources/auth.resource`
706
+
707
+ ```python
708
+ @name: "认证相关关键字"
709
+ @description: "处理登录、登出等认证操作"
710
+
711
+ function 用户登录 (用户名, 密码, 客户端="default") do
712
+ [打印], 内容: "模拟用户登录: ${用户名}"
713
+
714
+ # 模拟HTTP登录请求
715
+ [HTTP请求], 客户端: ${客户端}, 配置: '''
716
+ method: GET
717
+ url: https://jsonplaceholder.typicode.com/users/1
718
+ captures:
719
+ access_token: ["jsonpath", "$.id"]
720
+ user_id: ["jsonpath", "$.id"]
721
+ asserts:
722
+ - ["status", "eq", 200]
723
+ ''', 步骤名称: "用户登录: ${用户名}"
724
+
725
+ # 设置全局token供后续请求使用
726
+ [设置全局变量], 变量名: "auth_token", 值: ${access_token}
727
+ [设置全局变量], 变量名: "current_user_id", 值: ${user_id}
728
+
729
+ return ${access_token}
730
+ end
731
+
732
+ function 用户登出 (客户端="default") do
733
+ token = [获取全局变量], 变量名: "auth_token"
734
+ [打印], 内容: "模拟用户登出,token: ${token}"
735
+
736
+ # 模拟HTTP登出请求
737
+ [HTTP请求], 客户端: ${客户端}, 配置: '''
738
+ method: GET
739
+ url: https://jsonplaceholder.typicode.com/posts/1
740
+ asserts:
741
+ - ["status", "eq", 200]
742
+ ''', 步骤名称: "用户登出"
743
+
744
+ # 清除认证信息
745
+ [设置全局变量], 变量名: "auth_token", 值: ""
746
+ [设置全局变量], 变量名: "current_user_id", 值: ""
747
+ end
748
+ ```
749
+
750
+ #### 登录测试 `tests/auth/login.dsl`
751
+
752
+ ```python
753
+ @name: "用户登录功能测试"
754
+ @description: "测试用户登录的各种场景"
755
+ @tags: ["auth", "login"]
756
+ @import: "resources/auth.resource"
757
+
758
+ # 测试管理员登录
759
+ admin_token = [用户登录], 用户名: ${test_users.admin.username}, 密码: ${test_users.admin.password}
760
+ [断言], 条件: "${admin_token} != ''", 消息: "管理员登录失败"
761
+
762
+ # 验证登录状态(模拟)
763
+ [HTTP请求], 客户端: "default", 配置: '''
764
+ method: GET
765
+ url: https://jsonplaceholder.typicode.com/users/1
766
+ asserts:
767
+ - ["status", "eq", 200]
768
+ - ["jsonpath", "$.name", "exists"]
769
+ ''', 步骤名称: "验证登录状态"
770
+
771
+ # 测试登出
772
+ [用户登出]
773
+
774
+ teardown do
775
+ [打印], 内容: "登录测试完成"
776
+ end
777
+ ```
778
+
779
+ #### pytest集成 `test_runner.py`
780
+
781
+ ```python
782
+ from pytest_dsl.core.auto_decorator import auto_dsl
783
+
784
+ @auto_dsl("./tests")
785
+ class TestAPI:
786
+ """API自动化测试套件
787
+
788
+ 自动加载tests目录下的所有DSL文件
789
+ """
790
+ pass
791
+
792
+ @auto_dsl("./tests/auth")
793
+ class TestAuth:
794
+ """认证模块测试"""
795
+ pass
796
+
797
+ @auto_dsl("./tests/users")
798
+ class TestUsers:
799
+ """用户模块测试"""
800
+ pass
801
+ ```
802
+
803
+ #### 运行测试
804
+
805
+ ```bash
806
+ # 使用开发环境配置运行所有测试
807
+ pytest-dsl tests/ --yaml-vars config/dev.yaml
808
+
809
+ # 使用pytest运行(支持更多选项)
810
+ pytest test_runner.py --yaml-vars config/dev.yaml -v
811
+
812
+ # 生成Allure报告
813
+ pytest test_runner.py --yaml-vars config/dev.yaml --alluredir=reports
814
+ allure serve reports
815
+ ```
816
+
817
+ ## 🔧 扩展开发
818
+
819
+ ### 创建自定义关键字
820
+
821
+ pytest-dsl的强大之处在于可以轻松扩展自定义关键字:
822
+
823
+ #### 基础关键字开发
824
+
825
+ ```python
826
+ # keywords/my_keywords.py
827
+ from pytest_dsl.core.keyword_manager import keyword_manager
828
+
829
+ @keyword_manager.register('数据库查询', [
830
+ {'name': '查询语句', 'mapping': 'sql', 'description': 'SQL查询语句'},
831
+ {'name': '数据库', 'mapping': 'database', 'description': '数据库连接名', 'default': 'default'}
832
+ ])
833
+ def database_query(**kwargs):
834
+ """执行数据库查询"""
835
+ sql = kwargs.get('sql')
836
+ database = kwargs.get('database', 'default')
837
+ context = kwargs.get('context')
838
+
839
+ # 从上下文获取数据库配置
840
+ db_config = context.get_variable('database')
841
+
842
+ # 实现数据库查询逻辑
843
+ # connection = create_connection(db_config)
844
+ # result = connection.execute(sql)
845
+
846
+ # 模拟查询结果
847
+ result = [{"id": 1, "name": "test"}]
848
+
849
+ return result
850
+
851
+ @keyword_manager.register('发送邮件', [
852
+ {'name': '收件人', 'mapping': 'to_email', 'description': '收件人邮箱'},
853
+ {'name': '主题', 'mapping': 'subject', 'description': '邮件主题'},
854
+ {'name': '内容', 'mapping': 'content', 'description': '邮件内容'}
855
+ ])
856
+ def send_email(**kwargs):
857
+ """发送邮件通知"""
858
+ to_email = kwargs.get('to_email')
859
+ subject = kwargs.get('subject')
860
+ content = kwargs.get('content')
861
+
862
+ # 实现邮件发送逻辑
863
+ print(f"发送邮件到 {to_email}: {subject}")
864
+
865
+ return True
866
+ ```
867
+
868
+ #### 在DSL中使用自定义关键字
869
+
870
+ ```python
871
+ @name: "使用自定义关键字测试"
872
+
873
+ # 使用数据库查询关键字
874
+ users = [数据库查询], 查询语句: "SELECT * FROM users WHERE active = 1"
875
+ [打印], 内容: "查询到 ${len(users)} 个活跃用户"
876
+
877
+ # 发送测试报告邮件
878
+ [发送邮件], 收件人: "admin@example.com", 主题: "测试报告", 内容: "测试已完成"
879
+ ```
880
+
881
+ #### 支持远程模式的关键字
882
+
883
+ ```python
884
+ from pytest_dsl.core.keyword_manager import keyword_manager
885
+
886
+ @keyword_manager.register('文件操作', [
887
+ {'name': '操作类型', 'mapping': 'operation', 'description': '操作类型:read/write/delete'},
888
+ {'name': '文件路径', 'mapping': 'file_path', 'description': '文件路径'},
889
+ {'name': '内容', 'mapping': 'content', 'description': '文件内容(写入时使用)', 'default': ''}
890
+ ])
891
+ def file_operation(**kwargs):
892
+ """文件操作关键字,支持远程执行"""
893
+ operation = kwargs.get('operation')
894
+ file_path = kwargs.get('file_path')
895
+ content = kwargs.get('content', '')
896
+
897
+ if operation == 'read':
898
+ # 读取文件
899
+ with open(file_path, 'r', encoding='utf-8') as f:
900
+ return f.read()
901
+ elif operation == 'write':
902
+ # 写入文件
903
+ with open(file_path, 'w', encoding='utf-8') as f:
904
+ f.write(content)
905
+ return True
906
+ elif operation == 'delete':
907
+ # 删除文件
908
+ import os
909
+ os.remove(file_path)
910
+ return True
911
+
912
+ return False
913
+ ```
914
+
915
+ ### 关键字开发最佳实践
916
+
917
+ 1. **参数验证**:始终验证输入参数
918
+ 2. **错误处理**:提供清晰的错误信息
919
+ 3. **文档说明**:为每个关键字提供详细的文档
920
+ 4. **返回值**:确保关键字有明确的返回值
921
+ 5. **上下文使用**:合理使用context获取全局变量
922
+
923
+ ## 🚀 部署运维
924
+
925
+ ### 与pytest集成
926
+
927
+ ```python
928
+ # test_runner.py
929
+ from pytest_dsl.core.auto_decorator import auto_dsl
930
+
931
+ @auto_dsl("./tests")
932
+ class TestAPI:
933
+ """自动加载tests目录下的所有DSL文件"""
934
+ pass
935
+ ```
936
+
937
+ ### 生成测试报告
938
+
939
+ ```bash
940
+ # 生成Allure报告
941
+ pytest test_runner.py --alluredir=reports
942
+ allure serve reports
943
+
944
+ # 生成HTML报告
945
+ pytest test_runner.py --html=report.html --self-contained-html
946
+ ```
947
+
948
+ ### CI/CD集成
949
+
950
+ #### GitHub Actions示例
951
+
952
+ ```yaml
953
+ # .github/workflows/test.yml
954
+ name: API Tests
955
+
956
+ on: [push, pull_request]
957
+
958
+ jobs:
959
+ test:
960
+ runs-on: ubuntu-latest
961
+
962
+ steps:
963
+ - uses: actions/checkout@v2
964
+
965
+ - name: Set up Python
966
+ uses: actions/setup-python@v2
967
+ with:
968
+ python-version: '3.9'
969
+
970
+ - name: Install dependencies
971
+ run: |
972
+ pip install pytest-dsl allure-pytest
973
+
974
+ - name: Run tests
975
+ run: |
976
+ pytest-dsl tests/ --yaml-vars config/ci.yaml --alluredir=allure-results
977
+
978
+ - name: Generate report
979
+ uses: simple-elf/allure-report-action@master
980
+ if: always()
981
+ with:
982
+ allure_results: allure-results
983
+ allure_history: allure-history
984
+ ```
985
+
986
+ ### Docker部署
987
+
988
+ ```dockerfile
989
+ # Dockerfile
990
+ FROM python:3.9-slim
991
+
992
+ WORKDIR /app
993
+
994
+ COPY requirements.txt .
995
+ RUN pip install -r requirements.txt
996
+
997
+ COPY . .
998
+
999
+ CMD ["pytest-dsl", "tests/", "--yaml-vars", "config/prod.yaml"]
1000
+ ```
1001
+
1002
+ ## 📖 参考文档
1003
+
1004
+ ### 核心功能文档
1005
+ - [完整DSL语法指南](./docs/自动化关键字DSL语法设计.md)
1006
+ - [HTTP测试关键字详解](./docs/api.md)
1007
+ - [断言关键字使用指南](./docs/assertion_keywords.md)
1008
+ - [HTTP断言重试机制](./docs/http_assertion_retry.md)
1009
+
1010
+ ### 远程关键字文档
1011
+ - 📖 [远程关键字使用指南](./docs/remote-keywords-usage.md)
1012
+ - 🛠️ [远程关键字开发指南](./docs/remote-keywords-development.md)
1013
+ - 🔧 [远程服务器Hook机制](./docs/remote-hooks-guide.md)
1014
+ - ⚙️ [YAML远程服务器配置](./docs/yaml_remote_servers.md)
1015
+ - 🔄 [变量无缝传递功能](./docs/yaml_vars_seamless_sync.md)
1016
+
1017
+ ### 示例和最佳实践
1018
+ - [远程关键字验证示例](./examples/remote/)
1019
+ - [配置文件示例](./examples/config/)
1020
+
1021
+ ## 🎯 为什么选择pytest-dsl?
1022
+
1023
+ ### 核心优势
1024
+
1025
+ - **🎯 零门槛上手** - 自然语言风格,测试人员无需编程基础
1026
+ - **🔧 高度可扩展** - 轻松创建自定义关键字,适应任何测试场景
1027
+ - **🌐 分布式支持** - 内置远程关键字功能,支持大规模分布式测试
1028
+ - **🔄 无缝集成** - 完美兼容pytest生态,可渐进式迁移
1029
+ - **📊 丰富报告** - 集成Allure,提供专业级测试报告
1030
+ - **🛡️ 企业级特性** - 支持环境隔离、变量管理、安全认证
1031
+
1032
+ ### 适用场景
1033
+
1034
+ - **API接口测试** - 完整的HTTP测试支持
1035
+ - **微服务测试** - 分布式测试能力
1036
+ - **回归测试** - 数据驱动和批量执行
1037
+ - **集成测试** - 跨系统测试协调
1038
+ - **性能测试** - 结合其他工具进行性能测试
1039
+
1040
+ ## 🤝 贡献与支持
1041
+
1042
+ 我们欢迎您的贡献和反馈!
1043
+
1044
+ - 🐛 [报告问题](https://github.com/your-repo/pytest-dsl/issues)
1045
+ - 💡 [功能建议](https://github.com/your-repo/pytest-dsl/discussions)
1046
+ - 🔧 [提交PR](https://github.com/your-repo/pytest-dsl/pulls)
1047
+
1048
+ ## 📄 许可证
1049
+
1050
+ MIT License - 详见 [LICENSE](LICENSE) 文件
1051
+
1052
+ ---
1053
+
1054
+ ## 📋 示例验证
1055
+
1056
+ 本README.md中的大部分示例都已经过验证,确保可以正常运行。验证示例位于 `examples/readme_validation/` 目录中。
1057
+
1058
+ ### 运行验证
1059
+
1060
+ ```bash
1061
+ # 进入验证目录
1062
+ cd examples/readme_validation
1063
+
1064
+ # 运行所有验证示例
1065
+ python run_all_tests.py
1066
+
1067
+ # 或者运行单个示例
1068
+ pytest-dsl hello.dsl
1069
+ pytest-dsl api_basic.dsl
1070
+ ```
1071
+
1072
+ ### 验证覆盖
1073
+
1074
+ - ✅ 基础语法和内置关键字
1075
+ - ✅ 自定义关键字和资源文件
1076
+ - ✅ API测试功能
1077
+ - ✅ YAML配置管理
1078
+ - ✅ 变量访问语法
1079
+ - ✅ 断言重试机制
1080
+ - ✅ 认证功能示例
1081
+ - ✅ 数据驱动测试(pytest集成)
1082
+ - ✅ 布尔值支持和条件判断
1083
+ - ✅ 字典定义和嵌套访问
1084
+ - ✅ 循环控制语句(break/continue)
1085
+
1086
+ ---
1087
+
1088
+ 🚀 **开始使用pytest-dsl,让测试自动化变得简单而强大!**