pytest-dsl 0.11.1__py3-none-any.whl → 0.12.1__py3-none-any.whl
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/cli.py +89 -83
- pytest_dsl/core/http_client.py +91 -78
- pytest_dsl/core/http_request.py +135 -61
- pytest_dsl/examples/http/http_example.auto +1 -1
- pytest_dsl/examples/http/http_retry_assertions.auto +4 -4
- pytest_dsl/examples/http/http_retry_assertions_enhanced.auto +2 -2
- pytest_dsl/examples/http/new_retry_test.auto +1 -1
- pytest_dsl/examples/http/retry_assertions_only.auto +3 -3
- pytest_dsl/examples/http/retry_config_only.auto +3 -3
- pytest_dsl/examples/http/retry_debug.auto +19 -8
- pytest_dsl/examples/http/retry_with_fix.auto +1 -1
- pytest_dsl/keywords/http_keywords.py +42 -14
- pytest_dsl/keywords/system_keywords.py +420 -1
- pytest_dsl/plugin.py +3 -2
- {pytest_dsl-0.11.1.dist-info → pytest_dsl-0.12.1.dist-info}/METADATA +3 -3
- {pytest_dsl-0.11.1.dist-info → pytest_dsl-0.12.1.dist-info}/RECORD +20 -20
- {pytest_dsl-0.11.1.dist-info → pytest_dsl-0.12.1.dist-info}/WHEEL +0 -0
- {pytest_dsl-0.11.1.dist-info → pytest_dsl-0.12.1.dist-info}/entry_points.txt +0 -0
- {pytest_dsl-0.11.1.dist-info → pytest_dsl-0.12.1.dist-info}/licenses/LICENSE +0 -0
- {pytest_dsl-0.11.1.dist-info → pytest_dsl-0.12.1.dist-info}/top_level.txt +0 -0
@@ -130,7 +130,8 @@ def _load_file_content(file_path: str, is_template: bool = False,
|
|
130
130
|
|
131
131
|
|
132
132
|
def _process_request_config(config: Dict[str, Any],
|
133
|
-
test_context: TestContext = None) ->
|
133
|
+
test_context: TestContext = None) -> \
|
134
|
+
Dict[str, Any]:
|
134
135
|
"""处理请求配置,检查并处理文件引用
|
135
136
|
|
136
137
|
Args:
|
@@ -219,7 +220,15 @@ def _normalize_retry_config(config, assert_retry_count=None,
|
|
219
220
|
if 'indices' in retry_assertions:
|
220
221
|
standard_retry_config['indices'] = retry_assertions['indices']
|
221
222
|
if 'specific' in retry_assertions:
|
222
|
-
|
223
|
+
# 确保specific配置中的整数键被转换为字符串键,保持兼容性
|
224
|
+
specific_config = {}
|
225
|
+
for key, value in retry_assertions['specific'].items():
|
226
|
+
# 同时支持整数键和字符串键
|
227
|
+
specific_config[str(key)] = value
|
228
|
+
# 保留原始键类型以便查找
|
229
|
+
if isinstance(key, int):
|
230
|
+
specific_config[key] = value
|
231
|
+
standard_retry_config['specific'] = specific_config
|
223
232
|
|
224
233
|
# 处理传统retry配置(如果专用配置不存在)
|
225
234
|
elif 'retry' in config and config['retry']:
|
@@ -360,7 +369,17 @@ def http_request(context, **kwargs):
|
|
360
369
|
# 执行请求
|
361
370
|
response = http_req.execute(disable_auth=disable_auth)
|
362
371
|
|
363
|
-
#
|
372
|
+
# 统一处理断言逻辑
|
373
|
+
with allure.step("执行断言验证"):
|
374
|
+
if retry_config['enabled']:
|
375
|
+
# 使用统一的重试处理函数
|
376
|
+
_process_assertions_with_unified_retry(http_req, retry_config,
|
377
|
+
disable_auth)
|
378
|
+
else:
|
379
|
+
# 不需要重试,直接断言
|
380
|
+
http_req.process_asserts()
|
381
|
+
|
382
|
+
# 在断言完成后获取最终的捕获值(可能在重试期间被更新)
|
364
383
|
captured_values = http_req.captured_values
|
365
384
|
|
366
385
|
# 将捕获的变量注册到上下文
|
@@ -371,15 +390,6 @@ def http_request(context, **kwargs):
|
|
371
390
|
if save_response:
|
372
391
|
context.set(save_response, response)
|
373
392
|
|
374
|
-
# 统一处理断言逻辑
|
375
|
-
with allure.step("执行断言验证"):
|
376
|
-
if retry_config['enabled']:
|
377
|
-
# 使用统一的重试处理函数
|
378
|
-
_process_assertions_with_unified_retry(http_req, retry_config)
|
379
|
-
else:
|
380
|
-
# 不需要重试,直接断言
|
381
|
-
http_req.process_asserts()
|
382
|
-
|
383
393
|
# 获取会话状态(如果使用了会话)
|
384
394
|
session_state = None
|
385
395
|
if session_name:
|
@@ -447,7 +457,8 @@ def _deep_merge(dict1, dict2):
|
|
447
457
|
return dict1
|
448
458
|
|
449
459
|
|
450
|
-
def _process_assertions_with_unified_retry(http_req, retry_config
|
460
|
+
def _process_assertions_with_unified_retry(http_req, retry_config,
|
461
|
+
disable_auth=False):
|
451
462
|
"""使用统一的重试配置处理断言
|
452
463
|
|
453
464
|
Args:
|
@@ -482,6 +493,11 @@ def _process_assertions_with_unified_retry(http_req, retry_config):
|
|
482
493
|
# 临时替换配置
|
483
494
|
http_req.config = temp_config
|
484
495
|
|
496
|
+
# 确保在收集失败断言之前,response和captures是可用的
|
497
|
+
if not http_req.response:
|
498
|
+
# 如果response为空,重新执行一次请求
|
499
|
+
http_req.execute(disable_auth=disable_auth)
|
500
|
+
|
485
501
|
# 重新运行断言,这次只收集失败的断言而不抛出异常
|
486
502
|
_, failed_retryable_assertions = http_req.process_asserts()
|
487
503
|
except Exception as collect_err:
|
@@ -582,7 +598,17 @@ def _process_assertions_with_unified_retry(http_req, retry_config):
|
|
582
598
|
time.sleep(retry_interval)
|
583
599
|
|
584
600
|
# 重新发送请求
|
585
|
-
|
601
|
+
try:
|
602
|
+
http_req.execute(disable_auth=disable_auth)
|
603
|
+
except Exception as exec_error:
|
604
|
+
# 如果重新执行请求失败,记录错误并继续重试
|
605
|
+
allure.attach(
|
606
|
+
f"重试执行请求失败: {type(exec_error).__name__}: "
|
607
|
+
f"{str(exec_error)}",
|
608
|
+
name=f"重试请求执行失败 #{attempt}",
|
609
|
+
attachment_type=allure.attachment_type.TEXT
|
610
|
+
)
|
611
|
+
continue
|
586
612
|
|
587
613
|
# 过滤出仍在重试范围内的断言
|
588
614
|
still_retryable_assertions = []
|
@@ -657,6 +683,8 @@ def _process_assertions_with_unified_retry(http_req, retry_config):
|
|
657
683
|
)
|
658
684
|
|
659
685
|
try:
|
686
|
+
# 确保在最终断言之前重新执行一次请求
|
687
|
+
http_req.execute(disable_auth=disable_auth)
|
660
688
|
results, _ = http_req.process_asserts()
|
661
689
|
return results
|
662
690
|
except AssertionError as final_err:
|
@@ -185,7 +185,8 @@ def generate_random_number(**kwargs):
|
|
185
185
|
|
186
186
|
@keyword_manager.register('字符串操作', [
|
187
187
|
{'name': '操作', 'mapping': 'operation',
|
188
|
-
'description': '操作类型:拼接(concat)、替换(replace)、分割(split)
|
188
|
+
'description': '操作类型:拼接(concat)、替换(replace)、分割(split)、'
|
189
|
+
'大写(upper)、小写(lower)、去空格(strip)',
|
189
190
|
'default': 'strip'},
|
190
191
|
{'name': '字符串', 'mapping': 'string', 'description': '要操作的字符串'},
|
191
192
|
{'name': '参数1', 'mapping': 'param1',
|
@@ -366,3 +367,421 @@ def execute_command(**kwargs):
|
|
366
367
|
'stdout': '',
|
367
368
|
'stderr': str(e)
|
368
369
|
}
|
370
|
+
|
371
|
+
|
372
|
+
@keyword_manager.register('求和', [
|
373
|
+
{'name': '数据', 'mapping': 'data', 'description': '要求和的数字列表或可迭代对象'},
|
374
|
+
{'name': '起始值', 'mapping': 'start', 'description': '求和的起始值', 'default': 0}
|
375
|
+
])
|
376
|
+
def sum_values(**kwargs):
|
377
|
+
"""计算数字列表的总和
|
378
|
+
|
379
|
+
Args:
|
380
|
+
data: 要求和的数字列表
|
381
|
+
start: 求和的起始值,默认为0
|
382
|
+
|
383
|
+
Returns:
|
384
|
+
数字总和
|
385
|
+
"""
|
386
|
+
data = kwargs.get('data', [])
|
387
|
+
start = kwargs.get('start', 0)
|
388
|
+
|
389
|
+
# 确保data是可迭代的
|
390
|
+
if not hasattr(data, '__iter__') or isinstance(data, str):
|
391
|
+
raise ValueError("数据必须是可迭代的数字列表")
|
392
|
+
|
393
|
+
try:
|
394
|
+
result = sum(data, start)
|
395
|
+
|
396
|
+
with allure.step(f"求和计算: 数据长度={len(data)}, 起始值={start}"):
|
397
|
+
allure.attach(
|
398
|
+
f"输入数据: {data}\n起始值: {start}\n结果: {result}",
|
399
|
+
name="求和结果",
|
400
|
+
attachment_type=allure.attachment_type.TEXT
|
401
|
+
)
|
402
|
+
|
403
|
+
return result
|
404
|
+
except Exception as e:
|
405
|
+
allure.attach(
|
406
|
+
f"求和计算失败: {str(e)}",
|
407
|
+
name="求和错误",
|
408
|
+
attachment_type=allure.attachment_type.TEXT
|
409
|
+
)
|
410
|
+
raise
|
411
|
+
|
412
|
+
|
413
|
+
@keyword_manager.register('获取长度', [
|
414
|
+
{'name': '对象', 'mapping': 'obj', 'description': '要获取长度的对象(字符串、列表、字典等)'}
|
415
|
+
])
|
416
|
+
def get_length(**kwargs):
|
417
|
+
"""获取对象的长度
|
418
|
+
|
419
|
+
Args:
|
420
|
+
obj: 要获取长度的对象
|
421
|
+
|
422
|
+
Returns:
|
423
|
+
int: 对象的长度
|
424
|
+
"""
|
425
|
+
obj = kwargs.get('obj')
|
426
|
+
|
427
|
+
if obj is None:
|
428
|
+
return 0
|
429
|
+
|
430
|
+
try:
|
431
|
+
result = len(obj)
|
432
|
+
|
433
|
+
with allure.step(f"获取长度: 对象类型={type(obj).__name__}"):
|
434
|
+
allure.attach(
|
435
|
+
f"对象: {obj}\n类型: {type(obj).__name__}\n长度: {result}",
|
436
|
+
name="长度计算结果",
|
437
|
+
attachment_type=allure.attachment_type.TEXT
|
438
|
+
)
|
439
|
+
|
440
|
+
return result
|
441
|
+
except Exception as e:
|
442
|
+
allure.attach(
|
443
|
+
f"获取长度失败: {str(e)}",
|
444
|
+
name="长度计算错误",
|
445
|
+
attachment_type=allure.attachment_type.TEXT
|
446
|
+
)
|
447
|
+
raise
|
448
|
+
|
449
|
+
|
450
|
+
@keyword_manager.register('获取最大值', [
|
451
|
+
{'name': '数据', 'mapping': 'data', 'description': '要比较的数据列表或多个参数'},
|
452
|
+
{'name': '默认值', 'mapping': 'default',
|
453
|
+
'description': '当数据为空时的默认值', 'default': None}
|
454
|
+
])
|
455
|
+
def get_max_value(**kwargs):
|
456
|
+
"""获取最大值
|
457
|
+
|
458
|
+
Args:
|
459
|
+
data: 要比较的数据
|
460
|
+
default: 当数据为空时的默认值
|
461
|
+
|
462
|
+
Returns:
|
463
|
+
最大值
|
464
|
+
"""
|
465
|
+
data = kwargs.get('data')
|
466
|
+
default = kwargs.get('default')
|
467
|
+
|
468
|
+
if data is None:
|
469
|
+
if default is not None:
|
470
|
+
return default
|
471
|
+
raise ValueError("数据不能为空")
|
472
|
+
|
473
|
+
try:
|
474
|
+
# 如果data不是可迭代的,将其转换为列表
|
475
|
+
if not hasattr(data, '__iter__') or isinstance(data, str):
|
476
|
+
data = [data]
|
477
|
+
|
478
|
+
if len(data) == 0:
|
479
|
+
if default is not None:
|
480
|
+
return default
|
481
|
+
raise ValueError("数据列表为空")
|
482
|
+
|
483
|
+
result = max(data)
|
484
|
+
|
485
|
+
with allure.step(f"获取最大值: 数据长度={len(data)}"):
|
486
|
+
allure.attach(
|
487
|
+
f"输入数据: {data}\n最大值: {result}",
|
488
|
+
name="最大值计算结果",
|
489
|
+
attachment_type=allure.attachment_type.TEXT
|
490
|
+
)
|
491
|
+
|
492
|
+
return result
|
493
|
+
except Exception as e:
|
494
|
+
allure.attach(
|
495
|
+
f"获取最大值失败: {str(e)}",
|
496
|
+
name="最大值计算错误",
|
497
|
+
attachment_type=allure.attachment_type.TEXT
|
498
|
+
)
|
499
|
+
raise
|
500
|
+
|
501
|
+
|
502
|
+
@keyword_manager.register('获取最小值', [
|
503
|
+
{'name': '数据', 'mapping': 'data', 'description': '要比较的数据列表或多个参数'},
|
504
|
+
{'name': '默认值', 'mapping': 'default',
|
505
|
+
'description': '当数据为空时的默认值', 'default': None}
|
506
|
+
])
|
507
|
+
def get_min_value(**kwargs):
|
508
|
+
"""获取最小值
|
509
|
+
|
510
|
+
Args:
|
511
|
+
data: 要比较的数据
|
512
|
+
default: 当数据为空时的默认值
|
513
|
+
|
514
|
+
Returns:
|
515
|
+
最小值
|
516
|
+
"""
|
517
|
+
data = kwargs.get('data')
|
518
|
+
default = kwargs.get('default')
|
519
|
+
|
520
|
+
if data is None:
|
521
|
+
if default is not None:
|
522
|
+
return default
|
523
|
+
raise ValueError("数据不能为空")
|
524
|
+
|
525
|
+
try:
|
526
|
+
# 如果data不是可迭代的,将其转换为列表
|
527
|
+
if not hasattr(data, '__iter__') or isinstance(data, str):
|
528
|
+
data = [data]
|
529
|
+
|
530
|
+
if len(data) == 0:
|
531
|
+
if default is not None:
|
532
|
+
return default
|
533
|
+
raise ValueError("数据列表为空")
|
534
|
+
|
535
|
+
result = min(data)
|
536
|
+
|
537
|
+
with allure.step(f"获取最小值: 数据长度={len(data)}"):
|
538
|
+
allure.attach(
|
539
|
+
f"输入数据: {data}\n最小值: {result}",
|
540
|
+
name="最小值计算结果",
|
541
|
+
attachment_type=allure.attachment_type.TEXT
|
542
|
+
)
|
543
|
+
|
544
|
+
return result
|
545
|
+
except Exception as e:
|
546
|
+
allure.attach(
|
547
|
+
f"获取最小值失败: {str(e)}",
|
548
|
+
name="最小值计算错误",
|
549
|
+
attachment_type=allure.attachment_type.TEXT
|
550
|
+
)
|
551
|
+
raise
|
552
|
+
|
553
|
+
|
554
|
+
@keyword_manager.register('绝对值', [
|
555
|
+
{'name': '数值', 'mapping': 'number', 'description': '要计算绝对值的数字'}
|
556
|
+
])
|
557
|
+
def get_absolute_value(**kwargs):
|
558
|
+
"""计算数字的绝对值
|
559
|
+
|
560
|
+
Args:
|
561
|
+
number: 要计算绝对值的数字
|
562
|
+
|
563
|
+
Returns:
|
564
|
+
数字的绝对值
|
565
|
+
"""
|
566
|
+
number = kwargs.get('number')
|
567
|
+
|
568
|
+
if number is None:
|
569
|
+
raise ValueError("数值不能为空")
|
570
|
+
|
571
|
+
try:
|
572
|
+
result = abs(number)
|
573
|
+
|
574
|
+
with allure.step(f"计算绝对值: {number}"):
|
575
|
+
allure.attach(
|
576
|
+
f"输入数值: {number}\n绝对值: {result}",
|
577
|
+
name="绝对值计算结果",
|
578
|
+
attachment_type=allure.attachment_type.TEXT
|
579
|
+
)
|
580
|
+
|
581
|
+
return result
|
582
|
+
except Exception as e:
|
583
|
+
allure.attach(
|
584
|
+
f"计算绝对值失败: {str(e)}",
|
585
|
+
name="绝对值计算错误",
|
586
|
+
attachment_type=allure.attachment_type.TEXT
|
587
|
+
)
|
588
|
+
raise
|
589
|
+
|
590
|
+
|
591
|
+
@keyword_manager.register('四舍五入', [
|
592
|
+
{'name': '数值', 'mapping': 'number', 'description': '要四舍五入的数字'},
|
593
|
+
{'name': '小数位数', 'mapping': 'ndigits',
|
594
|
+
'description': '保留的小数位数', 'default': 0}
|
595
|
+
])
|
596
|
+
def round_number(**kwargs):
|
597
|
+
"""对数字进行四舍五入
|
598
|
+
|
599
|
+
Args:
|
600
|
+
number: 要四舍五入的数字
|
601
|
+
ndigits: 保留的小数位数,默认为0(整数)
|
602
|
+
|
603
|
+
Returns:
|
604
|
+
四舍五入后的数字
|
605
|
+
"""
|
606
|
+
number = kwargs.get('number')
|
607
|
+
ndigits = kwargs.get('ndigits', 0)
|
608
|
+
|
609
|
+
if number is None:
|
610
|
+
raise ValueError("数值不能为空")
|
611
|
+
|
612
|
+
try:
|
613
|
+
if ndigits == 0:
|
614
|
+
result = round(number)
|
615
|
+
else:
|
616
|
+
result = round(number, int(ndigits))
|
617
|
+
|
618
|
+
with allure.step(f"四舍五入: {number} -> {ndigits}位小数"):
|
619
|
+
allure.attach(
|
620
|
+
f"输入数值: {number}\n小数位数: {ndigits}\n结果: {result}",
|
621
|
+
name="四舍五入结果",
|
622
|
+
attachment_type=allure.attachment_type.TEXT
|
623
|
+
)
|
624
|
+
|
625
|
+
return result
|
626
|
+
except Exception as e:
|
627
|
+
allure.attach(
|
628
|
+
f"四舍五入失败: {str(e)}",
|
629
|
+
name="四舍五入错误",
|
630
|
+
attachment_type=allure.attachment_type.TEXT
|
631
|
+
)
|
632
|
+
raise
|
633
|
+
|
634
|
+
|
635
|
+
@keyword_manager.register('转换为字符串', [
|
636
|
+
{'name': '值', 'mapping': 'value', 'description': '要转换为字符串的值'}
|
637
|
+
])
|
638
|
+
def convert_to_string(**kwargs):
|
639
|
+
"""将值转换为字符串
|
640
|
+
|
641
|
+
Args:
|
642
|
+
value: 要转换的值
|
643
|
+
|
644
|
+
Returns:
|
645
|
+
str: 转换后的字符串
|
646
|
+
"""
|
647
|
+
value = kwargs.get('value')
|
648
|
+
|
649
|
+
try:
|
650
|
+
result = str(value)
|
651
|
+
|
652
|
+
with allure.step(f"转换为字符串: {type(value).__name__} -> str"):
|
653
|
+
allure.attach(
|
654
|
+
f"原始值: {value}\n原始类型: {type(value).__name__}\n"
|
655
|
+
f"转换结果: {result}\n结果类型: {type(result).__name__}",
|
656
|
+
name="字符串转换结果",
|
657
|
+
attachment_type=allure.attachment_type.TEXT
|
658
|
+
)
|
659
|
+
|
660
|
+
return result
|
661
|
+
except Exception as e:
|
662
|
+
allure.attach(
|
663
|
+
f"转换为字符串失败: {str(e)}",
|
664
|
+
name="字符串转换错误",
|
665
|
+
attachment_type=allure.attachment_type.TEXT
|
666
|
+
)
|
667
|
+
raise
|
668
|
+
|
669
|
+
|
670
|
+
@keyword_manager.register('转换为整数', [
|
671
|
+
{'name': '值', 'mapping': 'value', 'description': '要转换为整数的值'},
|
672
|
+
{'name': '进制', 'mapping': 'base',
|
673
|
+
'description': '数字进制(当值为字符串时)', 'default': 10}
|
674
|
+
])
|
675
|
+
def convert_to_integer(**kwargs):
|
676
|
+
"""将值转换为整数
|
677
|
+
|
678
|
+
Args:
|
679
|
+
value: 要转换的值
|
680
|
+
base: 数字进制(当值为字符串时),默认为10
|
681
|
+
|
682
|
+
Returns:
|
683
|
+
int: 转换后的整数
|
684
|
+
"""
|
685
|
+
value = kwargs.get('value')
|
686
|
+
base = kwargs.get('base', 10)
|
687
|
+
|
688
|
+
if value is None:
|
689
|
+
raise ValueError("值不能为空")
|
690
|
+
|
691
|
+
try:
|
692
|
+
# 如果指定了非10进制,将值转换为字符串再进行进制转换
|
693
|
+
if int(base) != 10:
|
694
|
+
value_str = str(value)
|
695
|
+
result = int(value_str, int(base))
|
696
|
+
else:
|
697
|
+
result = int(value)
|
698
|
+
|
699
|
+
with allure.step(f"转换为整数: {type(value).__name__} -> int"):
|
700
|
+
allure.attach(
|
701
|
+
f"原始值: {value}\n原始类型: {type(value).__name__}\n"
|
702
|
+
f"进制: {base}\n转换结果: {result}\n结果类型: {type(result).__name__}",
|
703
|
+
name="整数转换结果",
|
704
|
+
attachment_type=allure.attachment_type.TEXT
|
705
|
+
)
|
706
|
+
|
707
|
+
return result
|
708
|
+
except Exception as e:
|
709
|
+
allure.attach(
|
710
|
+
f"转换为整数失败: {str(e)}",
|
711
|
+
name="整数转换错误",
|
712
|
+
attachment_type=allure.attachment_type.TEXT
|
713
|
+
)
|
714
|
+
raise
|
715
|
+
|
716
|
+
|
717
|
+
@keyword_manager.register('转换为浮点数', [
|
718
|
+
{'name': '值', 'mapping': 'value', 'description': '要转换为浮点数的值'}
|
719
|
+
])
|
720
|
+
def convert_to_float(**kwargs):
|
721
|
+
"""将值转换为浮点数
|
722
|
+
|
723
|
+
Args:
|
724
|
+
value: 要转换的值
|
725
|
+
|
726
|
+
Returns:
|
727
|
+
float: 转换后的浮点数
|
728
|
+
"""
|
729
|
+
value = kwargs.get('value')
|
730
|
+
|
731
|
+
if value is None:
|
732
|
+
raise ValueError("值不能为空")
|
733
|
+
|
734
|
+
try:
|
735
|
+
result = float(value)
|
736
|
+
|
737
|
+
with allure.step(f"转换为浮点数: {type(value).__name__} -> float"):
|
738
|
+
allure.attach(
|
739
|
+
f"原始值: {value}\n原始类型: {type(value).__name__}\n"
|
740
|
+
f"转换结果: {result}\n结果类型: {type(result).__name__}",
|
741
|
+
name="浮点数转换结果",
|
742
|
+
attachment_type=allure.attachment_type.TEXT
|
743
|
+
)
|
744
|
+
|
745
|
+
return result
|
746
|
+
except Exception as e:
|
747
|
+
allure.attach(
|
748
|
+
f"转换为浮点数失败: {str(e)}",
|
749
|
+
name="浮点数转换错误",
|
750
|
+
attachment_type=allure.attachment_type.TEXT
|
751
|
+
)
|
752
|
+
raise
|
753
|
+
|
754
|
+
|
755
|
+
@keyword_manager.register('转换为布尔值', [
|
756
|
+
{'name': '值', 'mapping': 'value', 'description': '要转换为布尔值的值'}
|
757
|
+
])
|
758
|
+
def convert_to_boolean(**kwargs):
|
759
|
+
"""将值转换为布尔值
|
760
|
+
|
761
|
+
Args:
|
762
|
+
value: 要转换的值
|
763
|
+
|
764
|
+
Returns:
|
765
|
+
bool: 转换后的布尔值
|
766
|
+
"""
|
767
|
+
value = kwargs.get('value')
|
768
|
+
|
769
|
+
try:
|
770
|
+
result = bool(value)
|
771
|
+
|
772
|
+
with allure.step(f"转换为布尔值: {type(value).__name__} -> bool"):
|
773
|
+
allure.attach(
|
774
|
+
f"原始值: {value}\n原始类型: {type(value).__name__}\n"
|
775
|
+
f"转换结果: {result}\n结果类型: {type(result).__name__}",
|
776
|
+
name="布尔值转换结果",
|
777
|
+
attachment_type=allure.attachment_type.TEXT
|
778
|
+
)
|
779
|
+
|
780
|
+
return result
|
781
|
+
except Exception as e:
|
782
|
+
allure.attach(
|
783
|
+
f"转换为布尔值失败: {str(e)}",
|
784
|
+
name="布尔值转换错误",
|
785
|
+
attachment_type=allure.attachment_type.TEXT
|
786
|
+
)
|
787
|
+
raise
|
pytest_dsl/plugin.py
CHANGED
@@ -5,11 +5,12 @@
|
|
5
5
|
"""
|
6
6
|
import pytest
|
7
7
|
import os
|
8
|
-
from pathlib import Path
|
9
8
|
|
10
9
|
# 导入模块化组件
|
11
10
|
from pytest_dsl.core.yaml_loader import add_yaml_options, load_yaml_variables
|
12
|
-
from pytest_dsl.core.plugin_discovery import
|
11
|
+
from pytest_dsl.core.plugin_discovery import (
|
12
|
+
load_all_plugins, scan_local_keywords
|
13
|
+
)
|
13
14
|
from pytest_dsl.core.global_context import global_context
|
14
15
|
|
15
16
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: pytest-dsl
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.12.1
|
4
4
|
Summary: A DSL testing framework based on pytest
|
5
5
|
Author: Chen Shuanglin
|
6
6
|
License: MIT
|
@@ -1038,7 +1038,7 @@ jobs:
|
|
1038
1038
|
|
1039
1039
|
- name: Run tests
|
1040
1040
|
run: |
|
1041
|
-
pytest
|
1041
|
+
pytest test_runner.py --alluredir=allure-results
|
1042
1042
|
|
1043
1043
|
- name: Generate report
|
1044
1044
|
uses: simple-elf/allure-report-action@master
|
@@ -1097,7 +1097,7 @@ CMD ["pytest-dsl", "tests/", "--yaml-vars", "config/prod.yaml"]
|
|
1097
1097
|
### 适用场景
|
1098
1098
|
|
1099
1099
|
- **API接口测试** - 完整的HTTP测试支持
|
1100
|
-
-
|
1100
|
+
- **分布式测试** - 跨服务调用、服务间通信和分布式系统测试
|
1101
1101
|
- **回归测试** - 数据驱动和批量执行
|
1102
1102
|
- **集成测试** - 跨系统测试协调
|
1103
1103
|
- **性能测试** - 结合其他工具进行性能测试
|
@@ -1,8 +1,8 @@
|
|
1
1
|
pytest_dsl/__init__.py,sha256=FzwXGvmuvMhRBKxvCdh1h-yJ2wUOnDxcTbU4Nt5fHn8,301
|
2
|
-
pytest_dsl/cli.py,sha256=
|
2
|
+
pytest_dsl/cli.py,sha256=clpvDtQ18tJHl0DUKhoAfCNROE3TB_t59z0fOn5VL9M,33404
|
3
3
|
pytest_dsl/conftest_adapter.py,sha256=cevEb0oEZKTZfUrGe1-CmkFByxKhUtjuurBJP7kpLc0,149
|
4
4
|
pytest_dsl/main_adapter.py,sha256=pUIPN_EzY3JCDlYK7yF_OeLDVqni8vtG15G7gVzPJXg,181
|
5
|
-
pytest_dsl/plugin.py,sha256=
|
5
|
+
pytest_dsl/plugin.py,sha256=zA1Uf5xcdpYVFRHfEy4rCAB1zTdn3EhO8AbOFysOBm8,1173
|
6
6
|
pytest_dsl/core/__init__.py,sha256=ersUoxIWSrisxs9GX_STlH4XAbjNxAWUQB0NboaC5zI,322
|
7
7
|
pytest_dsl/core/auth_provider.py,sha256=IZfXXrr4Uuc8QHwRPvhHSzNa2fwrqhjYts1xh78D39Q,14861
|
8
8
|
pytest_dsl/core/auto_decorator.py,sha256=9Mga-GB4AzV5nkB6zpfaq8IuHa0KOH8LlFvnWyH_tnU,6623
|
@@ -12,8 +12,8 @@ pytest_dsl/core/custom_keyword_manager.py,sha256=UdlGUc_mT8hIwmU7LVf4wJLG-geChwY
|
|
12
12
|
pytest_dsl/core/dsl_executor.py,sha256=aEEfocFCFxNDN1puBFhQzL5fzw9eZx8BAyYJI5XSt4Y,30472
|
13
13
|
pytest_dsl/core/dsl_executor_utils.py,sha256=cFoR2p3qQ2pb-UhkoefleK-zbuFqf0aBLh2Rlp8Ebs4,2180
|
14
14
|
pytest_dsl/core/global_context.py,sha256=NcEcS2V61MT70tgAsGsFWQq0P3mKjtHQr1rgT3yTcyY,3535
|
15
|
-
pytest_dsl/core/http_client.py,sha256=
|
16
|
-
pytest_dsl/core/http_request.py,sha256=
|
15
|
+
pytest_dsl/core/http_client.py,sha256=hdx8gI4JCmq1-96pbiKeyKzSQUzPAi08cRNmljiPQmY,15536
|
16
|
+
pytest_dsl/core/http_request.py,sha256=6e-gTztH3wu2eSW27Nc0uPmyWjB6oBwndx8Vqnu5uyg,60030
|
17
17
|
pytest_dsl/core/keyword_manager.py,sha256=hoNXHQcumnufPRUobnY0mnku4CHxSg2amwPFby4gQEs,7643
|
18
18
|
pytest_dsl/core/lexer.py,sha256=WaLzt9IhtHiA90Fg2WGgfVztveCUhtgxzANBaEiy-F8,4347
|
19
19
|
pytest_dsl/core/parser.py,sha256=xxy6yC6NdwHxln200aIuaWWN3w44uI8kkNlw8PTVpYI,11855
|
@@ -39,16 +39,16 @@ pytest_dsl/examples/http/__init__.py,sha256=TCV9NzLmBbPDwNpLQs4_s_LlX9T47ApQ5UAr
|
|
39
39
|
pytest_dsl/examples/http/builtin_auth_test.auto,sha256=qZYpmxTBW018E5YQ4c8bbnoByEA3Y7BzpPfLJtDwMKQ,2374
|
40
40
|
pytest_dsl/examples/http/file_reference_test.auto,sha256=lHDxuR9NWp-DdASRUt5xNlcOn7FtmLu6oAGDvSsMpeo,3870
|
41
41
|
pytest_dsl/examples/http/http_advanced.auto,sha256=rVEtEY93rYcSrQJ7ntwW-GbHm8wltDobgd-KydIdVLU,2595
|
42
|
-
pytest_dsl/examples/http/http_example.auto,sha256=
|
42
|
+
pytest_dsl/examples/http/http_example.auto,sha256=g66-jQODonQeoVSRdggUIho6FGm5ON6Vqa0nuLDKG1o,4982
|
43
43
|
pytest_dsl/examples/http/http_length_test.auto,sha256=GYlFtSj0Nfk_9aqQrCXvRlwvTJHOk9CIf8X1CV_VKJg,1972
|
44
|
-
pytest_dsl/examples/http/http_retry_assertions.auto,sha256=
|
45
|
-
pytest_dsl/examples/http/http_retry_assertions_enhanced.auto,sha256=
|
44
|
+
pytest_dsl/examples/http/http_retry_assertions.auto,sha256=Dke5gkEZ9PTnKAhH86Umoo5DSh8P3RGjZN-gQSmrcBU,3385
|
45
|
+
pytest_dsl/examples/http/http_retry_assertions_enhanced.auto,sha256=Y9Lt6KO9z799na-jtZGv6nrskOlM3CXVGPVo82SEmHM,3195
|
46
46
|
pytest_dsl/examples/http/http_with_yaml.auto,sha256=u6Bw4EqQuQAMV-g2xS_iPmZwL4WRTpTqsJhL3xMRyfo,1942
|
47
|
-
pytest_dsl/examples/http/new_retry_test.auto,sha256=
|
48
|
-
pytest_dsl/examples/http/retry_assertions_only.auto,sha256=
|
49
|
-
pytest_dsl/examples/http/retry_config_only.auto,sha256=
|
50
|
-
pytest_dsl/examples/http/retry_debug.auto,sha256=
|
51
|
-
pytest_dsl/examples/http/retry_with_fix.auto,sha256=
|
47
|
+
pytest_dsl/examples/http/new_retry_test.auto,sha256=8ALnUYsZDI9TryQ2zDUg1JJYy7KGWKNQaECqure_0ck,669
|
48
|
+
pytest_dsl/examples/http/retry_assertions_only.auto,sha256=KhLnpGYIGz49VQZuXqcbKucEV4uW0mUWiQKg_MM80Vw,1887
|
49
|
+
pytest_dsl/examples/http/retry_config_only.auto,sha256=jGlpOcJS9IkN9OSe3Hs4uIq5EwxCzIg_72q5HFJK8NI,1482
|
50
|
+
pytest_dsl/examples/http/retry_debug.auto,sha256=5f32aYYpnNg98sl8-2arooHA4LI4870rkEGu8Z4UlE8,1136
|
51
|
+
pytest_dsl/examples/http/retry_with_fix.auto,sha256=GUn4T9oPsOQTsxScBRJD-M_sQkS3k2HDFYShtJNH-9s,693
|
52
52
|
pytest_dsl/examples/http/simple_retry.auto,sha256=Y7S8sR8VRjdk-iVJxcOD0Fb2tt_RO00BmA9eat3L0ks,569
|
53
53
|
pytest_dsl/examples/http/vars.yaml,sha256=GO0x6kpaUYk90TSOxtXh_3RYcacw8-ogBv5nG72Lr9Y,1186
|
54
54
|
pytest_dsl/examples/quickstart/api_basics.auto,sha256=SrDRBASqy5ZMXnmfczuKNCXoTijF54DChpeTrfsDtGQ,1544
|
@@ -57,17 +57,17 @@ pytest_dsl/examples/quickstart/loops.auto,sha256=ZNZ6qP636v8QMY8QRyTUBB43gWCsqHb
|
|
57
57
|
pytest_dsl/keywords/__init__.py,sha256=5aiyPU_t1UiB2MEZ6M9ffOKnV1mFT_2YHxnZvyWaBNI,372
|
58
58
|
pytest_dsl/keywords/assertion_keywords.py,sha256=obW06H_3AizsvEM_9VE2JVuwvgrNVqP1kUTDd3U1SMk,23240
|
59
59
|
pytest_dsl/keywords/global_keywords.py,sha256=4yw5yeXoGf_4W26F39EA2Pp-mH9GiKGy2jKgFO9a_wM,2509
|
60
|
-
pytest_dsl/keywords/http_keywords.py,sha256=
|
61
|
-
pytest_dsl/keywords/system_keywords.py,sha256=
|
60
|
+
pytest_dsl/keywords/http_keywords.py,sha256=baT_p04lOqZpfmOvBdPdzW2_KTikMEJmKPvpEx5sddU,28839
|
61
|
+
pytest_dsl/keywords/system_keywords.py,sha256=hjsACYER87rseSj4thBFnjDqe6At5hBT4Gjifj4ulDE,24470
|
62
62
|
pytest_dsl/remote/__init__.py,sha256=syRSxTlTUfdAPleJnVS4MykRyEN8_SKiqlsn6SlIK8k,120
|
63
63
|
pytest_dsl/remote/hook_manager.py,sha256=0hwRKP8yhcnfAnrrnZGVT-S0TBgo6c0A4qO5XRpvV1U,4899
|
64
64
|
pytest_dsl/remote/keyword_client.py,sha256=BL80MOaLroUi0v-9sLtkJ55g1R0Iw9SE1k11Ifwqx-I,17292
|
65
65
|
pytest_dsl/remote/keyword_server.py,sha256=vGIE3Bhh461xX_u1U-Cf5nrWL2GQFYdtQdcMWfFIYgE,22320
|
66
66
|
pytest_dsl/remote/variable_bridge.py,sha256=dv-d3Gq9ttvvrXM1fdlLtoSOPB6vRp0_GBOwX4wvcy8,7121
|
67
67
|
pytest_dsl/templates/keywords_report.html,sha256=7x84iq6hi08nf1iQ95jZ3izcAUPx6JFm0_8xS85CYws,31241
|
68
|
-
pytest_dsl-0.
|
69
|
-
pytest_dsl-0.
|
70
|
-
pytest_dsl-0.
|
71
|
-
pytest_dsl-0.
|
72
|
-
pytest_dsl-0.
|
73
|
-
pytest_dsl-0.
|
68
|
+
pytest_dsl-0.12.1.dist-info/licenses/LICENSE,sha256=Rguy8cb9sYhK6cmrBdXvwh94rKVDh2tVZEWptsHIsVM,1071
|
69
|
+
pytest_dsl-0.12.1.dist-info/METADATA,sha256=QHdli1Rdp1xywHV12X4Xo2BL1Xie2czRRtQJ_dPPXqc,29655
|
70
|
+
pytest_dsl-0.12.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
71
|
+
pytest_dsl-0.12.1.dist-info/entry_points.txt,sha256=PLOBbH02OGY1XR1JDKIZB1Em87loUvbgMRWaag-5FhY,204
|
72
|
+
pytest_dsl-0.12.1.dist-info/top_level.txt,sha256=4CrSx4uNqxj7NvK6k1y2JZrSrJSzi-UvPZdqpUhumWM,11
|
73
|
+
pytest_dsl-0.12.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|