pytest-dsl 0.5.0__tar.gz → 0.6.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_dsl-0.5.0/pytest_dsl.egg-info → pytest_dsl-0.6.0}/PKG-INFO +138 -9
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/README.md +137 -8
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pyproject.toml +1 -1
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/cli.py +28 -33
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/core/auto_decorator.py +72 -53
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/core/auto_directory.py +8 -5
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/core/dsl_executor.py +56 -11
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/core/http_request.py +272 -221
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/core/lexer.py +3 -13
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/core/parser.py +2 -2
- pytest_dsl-0.6.0/pytest_dsl/core/parsetab.py +114 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/core/plugin_discovery.py +1 -8
- pytest_dsl-0.6.0/pytest_dsl/core/yaml_loader.py +139 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/examples/assert/assertion_example.auto +1 -1
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/examples/assert/boolean_test.auto +2 -2
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/examples/assert/expression_test.auto +1 -1
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/examples/custom/test_advanced_keywords.auto +2 -2
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/examples/custom/test_custom_keywords.auto +2 -2
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/examples/custom/test_default_values.auto +2 -2
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/examples/http/file_reference_test.auto +1 -1
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/examples/http/http_advanced.auto +1 -1
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/examples/http/http_example.auto +1 -1
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/examples/http/http_length_test.auto +1 -1
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/examples/http/http_retry_assertions.auto +1 -1
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/examples/http/http_retry_assertions_enhanced.auto +2 -2
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/examples/http/http_with_yaml.auto +1 -1
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/examples/quickstart/api_basics.auto +1 -1
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/examples/quickstart/assertions.auto +1 -1
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/examples/quickstart/loops.auto +2 -2
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/keywords/assertion_keywords.py +76 -62
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/keywords/global_keywords.py +43 -4
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/keywords/http_keywords.py +58 -56
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0/pytest_dsl.egg-info}/PKG-INFO +138 -9
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl.egg-info/SOURCES.txt +6 -1
- pytest_dsl-0.6.0/tests/test_end_to_end_seamless.py +239 -0
- pytest_dsl-0.6.0/tests/test_http_assertions_extractors.py +378 -0
- pytest_dsl-0.6.0/tests/test_seamless_variable_sync.py +197 -0
- pytest_dsl-0.6.0/tests/test_variable_sync.py +142 -0
- pytest_dsl-0.6.0/tests/test_variable_sync_demo.py +136 -0
- pytest_dsl-0.5.0/pytest_dsl/core/parsetab.py +0 -114
- pytest_dsl-0.5.0/pytest_dsl/core/yaml_loader.py +0 -62
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/LICENSE +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/MANIFEST.in +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/__init__.py +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/conftest_adapter.py +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/core/__init__.py +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/core/auth_provider.py +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/core/context.py +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/core/custom_keyword_manager.py +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/core/dsl_executor_utils.py +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/core/global_context.py +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/core/http_client.py +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/core/keyword_manager.py +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/core/utils.py +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/core/variable_utils.py +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/core/yaml_vars.py +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/docs/custom_keywords.md +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/examples/__init__.py +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/examples/http/__init__.py +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/examples/http/builtin_auth_test.auto +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/examples/http/new_retry_test.auto +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/examples/http/retry_assertions_only.auto +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/examples/http/retry_config_only.auto +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/examples/http/retry_debug.auto +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/examples/http/retry_with_fix.auto +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/examples/http/simple_retry.auto +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/examples/http/vars.yaml +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/examples/test_assert.py +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/examples/test_custom_keyword.py +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/examples/test_http.py +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/examples/test_quickstart.py +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/keywords/__init__.py +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/keywords/system_keywords.py +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/main_adapter.py +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/parsetab.py +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl/plugin.py +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl.egg-info/dependency_links.txt +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl.egg-info/entry_points.txt +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl.egg-info/requires.txt +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/pytest_dsl.egg-info/top_level.txt +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/setup.cfg +0 -0
- {pytest_dsl-0.5.0 → pytest_dsl-0.6.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: pytest-dsl
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.6.0
|
4
4
|
Summary: A DSL testing framework based on pytest
|
5
5
|
Author: Chen Shuanglin
|
6
6
|
License: MIT
|
@@ -60,7 +60,7 @@ for i in range(1, 3) do
|
|
60
60
|
[打印],内容: "循环次数: ${i}"
|
61
61
|
end
|
62
62
|
|
63
|
-
|
63
|
+
teardown do
|
64
64
|
[打印],内容: "测试完成!"
|
65
65
|
end
|
66
66
|
```
|
@@ -147,7 +147,7 @@ pytest-dsl允许在DSL文件中直接定义自定义关键字,类似于编程
|
|
147
147
|
@name: "自定义关键字示例"
|
148
148
|
|
149
149
|
# 定义一个简单的关键字(函数)
|
150
|
-
|
150
|
+
function 拼接字符串 (前缀, 后缀="默认后缀") do
|
151
151
|
# 直接使用关键字参数
|
152
152
|
[打印],内容: "拼接前缀: ${前缀} 和后缀: ${后缀}"
|
153
153
|
|
@@ -186,7 +186,7 @@ end
|
|
186
186
|
@author: Felix
|
187
187
|
@date: 2024-06-11
|
188
188
|
|
189
|
-
|
189
|
+
function 拼接字符串 (前缀, 后缀="我是默认值哦") do
|
190
190
|
# 直接使用关键字参数
|
191
191
|
[打印],内容:'拼接前缀: ${前缀} 和后缀: ${后缀}'
|
192
192
|
|
@@ -198,7 +198,7 @@ end
|
|
198
198
|
return ${结果变量}
|
199
199
|
end
|
200
200
|
|
201
|
-
|
201
|
+
function 计算长度 (文本) do
|
202
202
|
# 在实际场景中,可能会使用更复杂的逻辑
|
203
203
|
[打印],内容:'计算文本: ${文本} 的长度'
|
204
204
|
长度 = 10 # 为简化示例,这里使用固定值
|
@@ -277,7 +277,7 @@ end
|
|
277
277
|
[断言],条件:'${拼接结果} == "你好, 世界"',消息:'字符串拼接不匹配'
|
278
278
|
[断言],条件:'${文本长度} == 10',消息:'长度不匹配'
|
279
279
|
|
280
|
-
|
280
|
+
teardown do
|
281
281
|
[打印],内容:'自定义关键字测试完成'
|
282
282
|
end
|
283
283
|
```
|
@@ -429,7 +429,7 @@ def call_microservice(**kwargs):
|
|
429
429
|
│ └── api_tests/ # DSL测试文件目录
|
430
430
|
│ ├── login.dsl
|
431
431
|
│ └── users.dsl
|
432
|
-
├──
|
432
|
+
├── config/ # 变量文件
|
433
433
|
│ ├── dev.yaml # 开发环境配置
|
434
434
|
│ └── prod.yaml # 生产环境配置
|
435
435
|
└── pytest.ini # pytest配置
|
@@ -505,6 +505,8 @@ pytest-dsl-server --api-key your_secret_key
|
|
505
505
|
|
506
506
|
### 远程关键字语法
|
507
507
|
|
508
|
+
#### 方式一:DSL中导入(适合临时使用)
|
509
|
+
|
508
510
|
```python
|
509
511
|
# 导入远程关键字服务器
|
510
512
|
@remote: "http://keyword-server:8270/" as machineone
|
@@ -515,6 +517,34 @@ machineone|[打印],内容: "这是通过远程服务器执行的关键字"
|
|
515
517
|
结果 = machineone|[拼接字符串],前缀: "Hello, ",后缀: "Remote World!"
|
516
518
|
```
|
517
519
|
|
520
|
+
#### 方式二:YAML配置自动加载(推荐用于全局配置)
|
521
|
+
|
522
|
+
在`config/vars.yaml`或其他YAML配置文件中添加:
|
523
|
+
|
524
|
+
```yaml
|
525
|
+
# 远程服务器配置
|
526
|
+
remote_servers:
|
527
|
+
main_server:
|
528
|
+
url: "http://localhost:8270/"
|
529
|
+
alias: "main"
|
530
|
+
api_key: "your_api_key_here" # 可选
|
531
|
+
sync_config: # 可选
|
532
|
+
sync_global_vars: true
|
533
|
+
sync_yaml_vars: true
|
534
|
+
|
535
|
+
backup_server:
|
536
|
+
url: "http://backup-host:8270/"
|
537
|
+
alias: "backup"
|
538
|
+
```
|
539
|
+
|
540
|
+
然后在DSL中直接使用:
|
541
|
+
|
542
|
+
```python
|
543
|
+
# 无需@remote导入,直接使用YAML中配置的服务器
|
544
|
+
main|[打印],内容: "使用主服务器"
|
545
|
+
backup|[打印],内容: "使用备用服务器"
|
546
|
+
```
|
547
|
+
|
518
548
|
### 远程关键字测试示例
|
519
549
|
|
520
550
|
```python
|
@@ -529,13 +559,104 @@ machineone|[打印],内容: "这是通过远程服务器执行的关键字"
|
|
529
559
|
|
530
560
|
# 基本打印测试
|
531
561
|
machineone|[打印],内容: "这是通过远程服务器执行的关键字"
|
562
|
+
```
|
563
|
+
|
564
|
+
## 无缝变量传递功能
|
565
|
+
|
566
|
+
pytest-dsl提供了革命性的无缝变量传递功能,客户端的变量会自动传递到服务端,服务端使用时完全透明,无需任何前缀或特殊语法。
|
567
|
+
|
568
|
+
### 功能特性
|
569
|
+
|
570
|
+
- **🔄 无缝传递**:客户端变量自动传递到远程服务器,服务端使用时无需前缀
|
571
|
+
- **🛡️ 智能过滤**:自动过滤敏感信息(password、secret、key等)
|
572
|
+
- **⚡ 零配置**:无需复杂的同步设置,开箱即用
|
573
|
+
- **🎯 优先级保持**:保持变量访问优先级(本地 > 同步)
|
574
|
+
- **🔒 完全隔离**:不同服务器的变量完全隔离,互不影响
|
575
|
+
|
576
|
+
### 基本使用
|
577
|
+
|
578
|
+
**客户端配置 (vars.yaml)**:
|
579
|
+
```yaml
|
580
|
+
# 全局变量
|
581
|
+
g_base_url: "https://api.example.com"
|
582
|
+
|
583
|
+
# HTTP客户端配置
|
584
|
+
http_clients:
|
585
|
+
default:
|
586
|
+
base_url: "${g_base_url}"
|
587
|
+
timeout: 30
|
588
|
+
|
589
|
+
# 测试数据
|
590
|
+
test_data:
|
591
|
+
username: "testuser"
|
592
|
+
email: "test@example.com"
|
593
|
+
|
594
|
+
# 敏感信息(会被自动过滤)
|
595
|
+
password: "secret123"
|
596
|
+
api_key: "sk-1234567890"
|
597
|
+
```
|
598
|
+
|
599
|
+
**DSL测试脚本**:
|
600
|
+
```python
|
601
|
+
# 导入远程关键字服务器(连接时自动传递变量)
|
602
|
+
@remote: "http://localhost:8270/" as remote_server
|
603
|
+
|
604
|
+
# 远程关键字直接使用客户端变量,无需前缀!
|
605
|
+
remote_server|[HTTP请求], 客户端: "default", 配置: '''
|
606
|
+
request:
|
607
|
+
method: GET
|
608
|
+
url: ${g_base_url}/api/data
|
609
|
+
headers:
|
610
|
+
X-User: ${test_data.username}
|
611
|
+
X-Email: ${test_data.email}
|
612
|
+
'''
|
613
|
+
|
614
|
+
# 全局变量也可以直接使用
|
615
|
+
remote_server|[打印], 内容: "API地址: ${g_base_url}"
|
616
|
+
```
|
617
|
+
|
618
|
+
### 配置选项
|
619
|
+
|
620
|
+
可以通过sync_config参数控制传递行为:
|
621
|
+
|
622
|
+
```python
|
623
|
+
# 自定义传递配置
|
624
|
+
sync_config = {
|
625
|
+
'sync_global_vars': True, # 是否传递全局变量
|
626
|
+
'sync_yaml_vars': True, # 是否传递YAML配置变量
|
627
|
+
}
|
628
|
+
|
629
|
+
# 使用自定义配置连接
|
630
|
+
client = RemoteKeywordClient(sync_config=sync_config)
|
631
|
+
```
|
632
|
+
|
633
|
+
### 应用场景
|
634
|
+
|
635
|
+
1. **🌐 跨环境测试**:客户端配置自动传递到不同环境的远程服务器
|
636
|
+
2. **🔧 配置统一管理**:HTTP客户端、数据库连接等配置在客户端统一管理
|
637
|
+
3. **🏢 企业级部署**:测试配置集中管理,远程执行节点自动获取
|
638
|
+
4. **🔒 安全隔离**:敏感信息自动过滤,确保安全性
|
639
|
+
5. **⚡ 性能优化**:计算密集型任务在远程高性能服务器执行,配置无缝传递
|
532
640
|
|
533
641
|
# 随机数生成测试
|
534
642
|
随机数 = [生成随机数],最小值: 1,最大值: 100
|
535
643
|
machineone|[打印],内容: "远程生成的随机数: ${随机数}"
|
536
644
|
```
|
537
645
|
|
538
|
-
|
646
|
+
### 远程关键字功能特性
|
647
|
+
|
648
|
+
远程关键字功能已经完全支持所有内置关键字,包括:
|
649
|
+
|
650
|
+
- ✅ **HTTP请求关键字**:完整支持变量捕获、会话管理和响应保存
|
651
|
+
- ✅ **断言关键字**:支持各种断言操作
|
652
|
+
- ✅ **全局变量管理**:远程和本地环境独立的全局变量空间
|
653
|
+
- ✅ **JSON操作**:JSON提取和断言功能
|
654
|
+
- ✅ **工具关键字**:随机数生成、字符串操作等
|
655
|
+
- ✅ **时间关键字**:时间获取和格式化
|
656
|
+
|
657
|
+
详细的使用指南和开发文档请参考:
|
658
|
+
- 📖 [远程关键字使用指南](./docs/remote-keywords-usage.md)
|
659
|
+
- 🛠️ [远程关键字开发指南](./docs/remote-keywords-development.md)
|
539
660
|
|
540
661
|
### 远程关键字服务安全性
|
541
662
|
|
@@ -576,12 +697,20 @@ machineone|[打印],内容: "远程生成的随机数: ${随机数}"
|
|
576
697
|
|
577
698
|
## 进阶文档
|
578
699
|
|
700
|
+
### 核心功能文档
|
579
701
|
- [完整DSL语法指南](./docs/自动化关键字DSL语法设计.md)
|
580
702
|
- [创建自定义关键字](./pytest_dsl/docs/custom_keywords.md)
|
581
703
|
- [HTTP测试关键字](./docs/api.md)
|
582
704
|
- [断言关键字详解](./docs/assertion_keywords.md)
|
583
705
|
- [HTTP断言重试机制](./docs/http_assertion_retry.md)
|
584
|
-
|
706
|
+
|
707
|
+
### 远程关键字文档
|
708
|
+
- 📖 [远程关键字使用指南](./docs/remote-keywords-usage.md) - 如何使用远程关键字功能
|
709
|
+
- 🛠️ [远程关键字开发指南](./docs/remote-keywords-development.md) - 如何开发支持远程模式的关键字
|
710
|
+
- 🔧 [远程服务器Hook机制指南](./docs/remote-hooks-guide.md) - 如何使用hook机制扩展远程服务器功能和实现自定义授权
|
711
|
+
- ⚙️ [YAML远程服务器配置指南](./docs/yaml_remote_servers.md) - 如何通过YAML配置自动加载远程服务器
|
712
|
+
- 🔄 [YAML变量无缝传递功能](./docs/yaml_vars_seamless_sync.md) - 如何实现客户端YAML变量的无缝传递
|
713
|
+
- [远程关键字语法示例](./docs/remote_syntax_example.md) - 基础语法示例
|
585
714
|
|
586
715
|
## 贡献与支持
|
587
716
|
|
@@ -29,7 +29,7 @@ for i in range(1, 3) do
|
|
29
29
|
[打印],内容: "循环次数: ${i}"
|
30
30
|
end
|
31
31
|
|
32
|
-
|
32
|
+
teardown do
|
33
33
|
[打印],内容: "测试完成!"
|
34
34
|
end
|
35
35
|
```
|
@@ -116,7 +116,7 @@ pytest-dsl允许在DSL文件中直接定义自定义关键字,类似于编程
|
|
116
116
|
@name: "自定义关键字示例"
|
117
117
|
|
118
118
|
# 定义一个简单的关键字(函数)
|
119
|
-
|
119
|
+
function 拼接字符串 (前缀, 后缀="默认后缀") do
|
120
120
|
# 直接使用关键字参数
|
121
121
|
[打印],内容: "拼接前缀: ${前缀} 和后缀: ${后缀}"
|
122
122
|
|
@@ -155,7 +155,7 @@ end
|
|
155
155
|
@author: Felix
|
156
156
|
@date: 2024-06-11
|
157
157
|
|
158
|
-
|
158
|
+
function 拼接字符串 (前缀, 后缀="我是默认值哦") do
|
159
159
|
# 直接使用关键字参数
|
160
160
|
[打印],内容:'拼接前缀: ${前缀} 和后缀: ${后缀}'
|
161
161
|
|
@@ -167,7 +167,7 @@ end
|
|
167
167
|
return ${结果变量}
|
168
168
|
end
|
169
169
|
|
170
|
-
|
170
|
+
function 计算长度 (文本) do
|
171
171
|
# 在实际场景中,可能会使用更复杂的逻辑
|
172
172
|
[打印],内容:'计算文本: ${文本} 的长度'
|
173
173
|
长度 = 10 # 为简化示例,这里使用固定值
|
@@ -246,7 +246,7 @@ end
|
|
246
246
|
[断言],条件:'${拼接结果} == "你好, 世界"',消息:'字符串拼接不匹配'
|
247
247
|
[断言],条件:'${文本长度} == 10',消息:'长度不匹配'
|
248
248
|
|
249
|
-
|
249
|
+
teardown do
|
250
250
|
[打印],内容:'自定义关键字测试完成'
|
251
251
|
end
|
252
252
|
```
|
@@ -398,7 +398,7 @@ def call_microservice(**kwargs):
|
|
398
398
|
│ └── api_tests/ # DSL测试文件目录
|
399
399
|
│ ├── login.dsl
|
400
400
|
│ └── users.dsl
|
401
|
-
├──
|
401
|
+
├── config/ # 变量文件
|
402
402
|
│ ├── dev.yaml # 开发环境配置
|
403
403
|
│ └── prod.yaml # 生产环境配置
|
404
404
|
└── pytest.ini # pytest配置
|
@@ -474,6 +474,8 @@ pytest-dsl-server --api-key your_secret_key
|
|
474
474
|
|
475
475
|
### 远程关键字语法
|
476
476
|
|
477
|
+
#### 方式一:DSL中导入(适合临时使用)
|
478
|
+
|
477
479
|
```python
|
478
480
|
# 导入远程关键字服务器
|
479
481
|
@remote: "http://keyword-server:8270/" as machineone
|
@@ -484,6 +486,34 @@ machineone|[打印],内容: "这是通过远程服务器执行的关键字"
|
|
484
486
|
结果 = machineone|[拼接字符串],前缀: "Hello, ",后缀: "Remote World!"
|
485
487
|
```
|
486
488
|
|
489
|
+
#### 方式二:YAML配置自动加载(推荐用于全局配置)
|
490
|
+
|
491
|
+
在`config/vars.yaml`或其他YAML配置文件中添加:
|
492
|
+
|
493
|
+
```yaml
|
494
|
+
# 远程服务器配置
|
495
|
+
remote_servers:
|
496
|
+
main_server:
|
497
|
+
url: "http://localhost:8270/"
|
498
|
+
alias: "main"
|
499
|
+
api_key: "your_api_key_here" # 可选
|
500
|
+
sync_config: # 可选
|
501
|
+
sync_global_vars: true
|
502
|
+
sync_yaml_vars: true
|
503
|
+
|
504
|
+
backup_server:
|
505
|
+
url: "http://backup-host:8270/"
|
506
|
+
alias: "backup"
|
507
|
+
```
|
508
|
+
|
509
|
+
然后在DSL中直接使用:
|
510
|
+
|
511
|
+
```python
|
512
|
+
# 无需@remote导入,直接使用YAML中配置的服务器
|
513
|
+
main|[打印],内容: "使用主服务器"
|
514
|
+
backup|[打印],内容: "使用备用服务器"
|
515
|
+
```
|
516
|
+
|
487
517
|
### 远程关键字测试示例
|
488
518
|
|
489
519
|
```python
|
@@ -498,13 +528,104 @@ machineone|[打印],内容: "这是通过远程服务器执行的关键字"
|
|
498
528
|
|
499
529
|
# 基本打印测试
|
500
530
|
machineone|[打印],内容: "这是通过远程服务器执行的关键字"
|
531
|
+
```
|
532
|
+
|
533
|
+
## 无缝变量传递功能
|
534
|
+
|
535
|
+
pytest-dsl提供了革命性的无缝变量传递功能,客户端的变量会自动传递到服务端,服务端使用时完全透明,无需任何前缀或特殊语法。
|
536
|
+
|
537
|
+
### 功能特性
|
538
|
+
|
539
|
+
- **🔄 无缝传递**:客户端变量自动传递到远程服务器,服务端使用时无需前缀
|
540
|
+
- **🛡️ 智能过滤**:自动过滤敏感信息(password、secret、key等)
|
541
|
+
- **⚡ 零配置**:无需复杂的同步设置,开箱即用
|
542
|
+
- **🎯 优先级保持**:保持变量访问优先级(本地 > 同步)
|
543
|
+
- **🔒 完全隔离**:不同服务器的变量完全隔离,互不影响
|
544
|
+
|
545
|
+
### 基本使用
|
546
|
+
|
547
|
+
**客户端配置 (vars.yaml)**:
|
548
|
+
```yaml
|
549
|
+
# 全局变量
|
550
|
+
g_base_url: "https://api.example.com"
|
551
|
+
|
552
|
+
# HTTP客户端配置
|
553
|
+
http_clients:
|
554
|
+
default:
|
555
|
+
base_url: "${g_base_url}"
|
556
|
+
timeout: 30
|
557
|
+
|
558
|
+
# 测试数据
|
559
|
+
test_data:
|
560
|
+
username: "testuser"
|
561
|
+
email: "test@example.com"
|
562
|
+
|
563
|
+
# 敏感信息(会被自动过滤)
|
564
|
+
password: "secret123"
|
565
|
+
api_key: "sk-1234567890"
|
566
|
+
```
|
567
|
+
|
568
|
+
**DSL测试脚本**:
|
569
|
+
```python
|
570
|
+
# 导入远程关键字服务器(连接时自动传递变量)
|
571
|
+
@remote: "http://localhost:8270/" as remote_server
|
572
|
+
|
573
|
+
# 远程关键字直接使用客户端变量,无需前缀!
|
574
|
+
remote_server|[HTTP请求], 客户端: "default", 配置: '''
|
575
|
+
request:
|
576
|
+
method: GET
|
577
|
+
url: ${g_base_url}/api/data
|
578
|
+
headers:
|
579
|
+
X-User: ${test_data.username}
|
580
|
+
X-Email: ${test_data.email}
|
581
|
+
'''
|
582
|
+
|
583
|
+
# 全局变量也可以直接使用
|
584
|
+
remote_server|[打印], 内容: "API地址: ${g_base_url}"
|
585
|
+
```
|
586
|
+
|
587
|
+
### 配置选项
|
588
|
+
|
589
|
+
可以通过sync_config参数控制传递行为:
|
590
|
+
|
591
|
+
```python
|
592
|
+
# 自定义传递配置
|
593
|
+
sync_config = {
|
594
|
+
'sync_global_vars': True, # 是否传递全局变量
|
595
|
+
'sync_yaml_vars': True, # 是否传递YAML配置变量
|
596
|
+
}
|
597
|
+
|
598
|
+
# 使用自定义配置连接
|
599
|
+
client = RemoteKeywordClient(sync_config=sync_config)
|
600
|
+
```
|
601
|
+
|
602
|
+
### 应用场景
|
603
|
+
|
604
|
+
1. **🌐 跨环境测试**:客户端配置自动传递到不同环境的远程服务器
|
605
|
+
2. **🔧 配置统一管理**:HTTP客户端、数据库连接等配置在客户端统一管理
|
606
|
+
3. **🏢 企业级部署**:测试配置集中管理,远程执行节点自动获取
|
607
|
+
4. **🔒 安全隔离**:敏感信息自动过滤,确保安全性
|
608
|
+
5. **⚡ 性能优化**:计算密集型任务在远程高性能服务器执行,配置无缝传递
|
501
609
|
|
502
610
|
# 随机数生成测试
|
503
611
|
随机数 = [生成随机数],最小值: 1,最大值: 100
|
504
612
|
machineone|[打印],内容: "远程生成的随机数: ${随机数}"
|
505
613
|
```
|
506
614
|
|
507
|
-
|
615
|
+
### 远程关键字功能特性
|
616
|
+
|
617
|
+
远程关键字功能已经完全支持所有内置关键字,包括:
|
618
|
+
|
619
|
+
- ✅ **HTTP请求关键字**:完整支持变量捕获、会话管理和响应保存
|
620
|
+
- ✅ **断言关键字**:支持各种断言操作
|
621
|
+
- ✅ **全局变量管理**:远程和本地环境独立的全局变量空间
|
622
|
+
- ✅ **JSON操作**:JSON提取和断言功能
|
623
|
+
- ✅ **工具关键字**:随机数生成、字符串操作等
|
624
|
+
- ✅ **时间关键字**:时间获取和格式化
|
625
|
+
|
626
|
+
详细的使用指南和开发文档请参考:
|
627
|
+
- 📖 [远程关键字使用指南](./docs/remote-keywords-usage.md)
|
628
|
+
- 🛠️ [远程关键字开发指南](./docs/remote-keywords-development.md)
|
508
629
|
|
509
630
|
### 远程关键字服务安全性
|
510
631
|
|
@@ -545,12 +666,20 @@ machineone|[打印],内容: "远程生成的随机数: ${随机数}"
|
|
545
666
|
|
546
667
|
## 进阶文档
|
547
668
|
|
669
|
+
### 核心功能文档
|
548
670
|
- [完整DSL语法指南](./docs/自动化关键字DSL语法设计.md)
|
549
671
|
- [创建自定义关键字](./pytest_dsl/docs/custom_keywords.md)
|
550
672
|
- [HTTP测试关键字](./docs/api.md)
|
551
673
|
- [断言关键字详解](./docs/assertion_keywords.md)
|
552
674
|
- [HTTP断言重试机制](./docs/http_assertion_retry.md)
|
553
|
-
|
675
|
+
|
676
|
+
### 远程关键字文档
|
677
|
+
- 📖 [远程关键字使用指南](./docs/remote-keywords-usage.md) - 如何使用远程关键字功能
|
678
|
+
- 🛠️ [远程关键字开发指南](./docs/remote-keywords-development.md) - 如何开发支持远程模式的关键字
|
679
|
+
- 🔧 [远程服务器Hook机制指南](./docs/remote-hooks-guide.md) - 如何使用hook机制扩展远程服务器功能和实现自定义授权
|
680
|
+
- ⚙️ [YAML远程服务器配置指南](./docs/yaml_remote_servers.md) - 如何通过YAML配置自动加载远程服务器
|
681
|
+
- 🔄 [YAML变量无缝传递功能](./docs/yaml_vars_seamless_sync.md) - 如何实现客户端YAML变量的无缝传递
|
682
|
+
- [远程关键字语法示例](./docs/remote_syntax_example.md) - 基础语法示例
|
554
683
|
|
555
684
|
## 贡献与支持
|
556
685
|
|
@@ -13,8 +13,9 @@ from pathlib import Path
|
|
13
13
|
from pytest_dsl.core.lexer import get_lexer
|
14
14
|
from pytest_dsl.core.parser import get_parser
|
15
15
|
from pytest_dsl.core.dsl_executor import DSLExecutor
|
16
|
-
from pytest_dsl.core.
|
16
|
+
from pytest_dsl.core.yaml_loader import load_yaml_variables_from_args
|
17
17
|
from pytest_dsl.core.auto_directory import SETUP_FILE_NAME, TEARDOWN_FILE_NAME, execute_hook_file
|
18
|
+
from pytest_dsl.core.plugin_discovery import load_all_plugins
|
18
19
|
|
19
20
|
|
20
21
|
def read_file(filename):
|
@@ -27,35 +28,26 @@ def parse_args():
|
|
27
28
|
"""解析命令行参数"""
|
28
29
|
parser = argparse.ArgumentParser(description='执行DSL测试文件')
|
29
30
|
parser.add_argument('path', help='要执行的DSL文件路径或包含DSL文件的目录')
|
30
|
-
parser.add_argument('--yaml-vars', action='append', default=[],
|
31
|
+
parser.add_argument('--yaml-vars', action='append', default=[],
|
31
32
|
help='YAML变量文件路径,可以指定多个文件 (例如: --yaml-vars vars1.yaml --yaml-vars vars2.yaml)')
|
32
33
|
parser.add_argument('--yaml-vars-dir', default=None,
|
33
34
|
help='YAML变量文件目录路径,将加载该目录下所有.yaml文件')
|
34
|
-
|
35
|
+
|
35
36
|
return parser.parse_args()
|
36
37
|
|
37
38
|
|
38
39
|
def load_yaml_variables(args):
|
39
40
|
"""从命令行参数加载YAML变量"""
|
40
|
-
#
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
print(f"已加载YAML变量目录: {yaml_vars_dir}")
|
51
|
-
loaded_files = yaml_vars.get_loaded_files()
|
52
|
-
if loaded_files:
|
53
|
-
dir_files = [f for f in loaded_files if Path(f).parent == Path(yaml_vars_dir)]
|
54
|
-
if dir_files:
|
55
|
-
print(f"目录中加载的文件: {', '.join(dir_files)}")
|
56
|
-
except NotADirectoryError:
|
57
|
-
print(f"YAML变量目录不存在: {yaml_vars_dir}")
|
58
|
-
sys.exit(1)
|
41
|
+
# 使用统一的加载函数,包含远程服务器自动连接功能
|
42
|
+
try:
|
43
|
+
load_yaml_variables_from_args(
|
44
|
+
yaml_files=args.yaml_vars,
|
45
|
+
yaml_vars_dir=args.yaml_vars_dir,
|
46
|
+
project_root=os.getcwd() # CLI模式下使用当前工作目录作为项目根目录
|
47
|
+
)
|
48
|
+
except Exception as e:
|
49
|
+
print(f"加载YAML变量失败: {str(e)}")
|
50
|
+
sys.exit(1)
|
59
51
|
|
60
52
|
|
61
53
|
def execute_dsl_file(file_path, lexer, parser, executor):
|
@@ -85,14 +77,17 @@ def main():
|
|
85
77
|
"""命令行入口点"""
|
86
78
|
args = parse_args()
|
87
79
|
path = args.path
|
88
|
-
|
89
|
-
#
|
80
|
+
|
81
|
+
# 加载内置关键字插件
|
82
|
+
load_all_plugins()
|
83
|
+
|
84
|
+
# 加载YAML变量(包括远程服务器自动连接)
|
90
85
|
load_yaml_variables(args)
|
91
|
-
|
86
|
+
|
92
87
|
lexer = get_lexer()
|
93
88
|
parser = get_parser()
|
94
89
|
executor = DSLExecutor()
|
95
|
-
|
90
|
+
|
96
91
|
# 检查路径是文件还是目录
|
97
92
|
if os.path.isfile(path):
|
98
93
|
# 执行单个文件
|
@@ -102,32 +97,32 @@ def main():
|
|
102
97
|
elif os.path.isdir(path):
|
103
98
|
# 执行目录中的所有DSL文件
|
104
99
|
print(f"执行目录: {path}")
|
105
|
-
|
100
|
+
|
106
101
|
# 先执行目录的setup文件(如果存在)
|
107
102
|
setup_file = os.path.join(path, SETUP_FILE_NAME)
|
108
103
|
if os.path.exists(setup_file):
|
109
104
|
execute_hook_file(Path(setup_file), True, path)
|
110
|
-
|
105
|
+
|
111
106
|
# 查找并执行所有DSL文件
|
112
107
|
dsl_files = find_dsl_files(path)
|
113
108
|
if not dsl_files:
|
114
109
|
print(f"目录中没有找到DSL文件: {path}")
|
115
110
|
sys.exit(1)
|
116
|
-
|
111
|
+
|
117
112
|
print(f"找到 {len(dsl_files)} 个DSL文件")
|
118
|
-
|
113
|
+
|
119
114
|
# 执行所有DSL文件
|
120
115
|
failures = 0
|
121
116
|
for file_path in dsl_files:
|
122
117
|
success = execute_dsl_file(file_path, lexer, parser, executor)
|
123
118
|
if not success:
|
124
119
|
failures += 1
|
125
|
-
|
120
|
+
|
126
121
|
# 最后执行目录的teardown文件(如果存在)
|
127
122
|
teardown_file = os.path.join(path, TEARDOWN_FILE_NAME)
|
128
123
|
if os.path.exists(teardown_file):
|
129
124
|
execute_hook_file(Path(teardown_file), False, path)
|
130
|
-
|
125
|
+
|
131
126
|
# 如果有失败的测试,返回非零退出码
|
132
127
|
if failures > 0:
|
133
128
|
print(f"总计 {failures}/{len(dsl_files)} 个测试失败")
|
@@ -140,4 +135,4 @@ def main():
|
|
140
135
|
|
141
136
|
|
142
137
|
if __name__ == '__main__':
|
143
|
-
main()
|
138
|
+
main()
|