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