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.
@@ -1,8 +1,8 @@
1
1
  import json
2
+ import logging
2
3
  import re
3
- import yaml
4
4
  import jsonpath_ng.ext as jsonpath
5
- from typing import Dict, List, Any, Union, Optional, Tuple, Set
5
+ from typing import Dict, List, Any, Tuple
6
6
  import lxml.etree as etree
7
7
  from requests import Response
8
8
  import allure
@@ -10,6 +10,7 @@ import requests
10
10
 
11
11
  from pytest_dsl.core.http_client import http_client_manager
12
12
 
13
+ logger = logging.getLogger(__name__)
13
14
 
14
15
  # 定义支持的比较操作符
15
16
  COMPARISON_OPERATORS = {
@@ -25,7 +26,8 @@ ASSERTION_TYPES = {
25
26
 
26
27
  # 操作符和断言类型重叠的集合(可以作为二者使用的)
27
28
  DUAL_PURPOSE = {
28
- "contains", "not_contains", "matches", "in", "not_in", "startswith", "endswith"
29
+ "contains", "not_contains", "matches", "in", "not_in", "startswith",
30
+ "endswith"
29
31
  }
30
32
 
31
33
 
@@ -35,7 +37,8 @@ class HTTPRequest:
35
37
  负责处理HTTP请求、响应捕获和断言
36
38
  """
37
39
 
38
- def __init__(self, config: Dict[str, Any], client_name: str = "default", session_name: str = None):
40
+ def __init__(self, config: Dict[str, Any], client_name: str = "default",
41
+ session_name: str = None):
39
42
  """初始化HTTP请求
40
43
 
41
44
  Args:
@@ -60,7 +63,8 @@ class HTTPRequest:
60
63
  """
61
64
  # 获取HTTP客户端
62
65
  if self.session_name:
63
- client = http_client_manager.get_session(self.session_name, self.client_name)
66
+ client = http_client_manager.get_session(
67
+ self.session_name, self.client_name)
64
68
  else:
65
69
  client = http_client_manager.get_client(self.client_name)
66
70
 
@@ -91,7 +95,8 @@ class HTTPRequest:
91
95
  'data': request_config.get('data'),
92
96
  'files': request_config.get('files'),
93
97
  'cookies': request_config.get('cookies'),
94
- 'auth': tuple(request_config.get('auth')) if request_config.get('auth') else None,
98
+ 'auth': (tuple(request_config.get('auth'))
99
+ if request_config.get('auth') else None),
95
100
  'timeout': request_config.get('timeout'),
96
101
  'allow_redirects': request_config.get('allow_redirects'),
97
102
  'verify': request_config.get('verify'),
@@ -101,7 +106,8 @@ class HTTPRequest:
101
106
  }
102
107
 
103
108
  # 过滤掉None值
104
- request_kwargs = {k: v for k, v in request_kwargs.items() if v is not None}
109
+ request_kwargs = {k: v for k,
110
+ v in request_kwargs.items() if v is not None}
105
111
 
106
112
  # 使用Allure记录请求信息
107
113
  self._log_request_to_allure(method, url, request_kwargs)
@@ -113,8 +119,21 @@ class HTTPRequest:
113
119
  # 使用Allure记录响应信息
114
120
  self._log_response_to_allure(self.response)
115
121
 
116
- # 处理捕获
117
- self.process_captures()
122
+ # 处理捕获 - 确保即使后续断言失败,捕获的变量也能被正确设置
123
+ try:
124
+ self.process_captures()
125
+ except Exception as capture_error:
126
+ # 捕获处理失败不应该影响请求的执行,只记录警告
127
+ # 但确保captured_values至少是空字典
128
+ if (not hasattr(self, 'captured_values') or
129
+ self.captured_values is None):
130
+ self.captured_values = {}
131
+ logger.warning(f"变量捕获处理失败: {str(capture_error)}")
132
+ allure.attach(
133
+ f"变量捕获处理失败: {str(capture_error)}",
134
+ name="变量捕获警告",
135
+ attachment_type=allure.attachment_type.TEXT
136
+ )
118
137
 
119
138
  return self.response
120
139
  except requests.exceptions.RequestException as e:
@@ -149,13 +168,16 @@ class HTTPRequest:
149
168
  Raises:
150
169
  ValueError: 如果响应对象不存在
151
170
  """
152
- if not self.response:
171
+ if self.response is None:
153
172
  error_message = f"需要先执行请求才能{operation}"
154
173
  # 记录更详细的错误信息到Allure
174
+ config_json = json.dumps(
175
+ self.config, indent=2, ensure_ascii=False, default=str)
155
176
  debug_info = (
156
177
  f"错误详情: self.response 为 None\n"
157
- f"配置信息: {json.dumps(self.config, indent=2, ensure_ascii=False, default=str)}\n"
158
- f"当前状态: 客户端名称={self.client_name}, 会话名称={self.session_name}"
178
+ f"配置信息: {config_json}\n"
179
+ f"当前状态: 客户端名称={self.client_name}, "
180
+ f"会话名称={self.session_name}"
159
181
  )
160
182
  allure.attach(
161
183
  debug_info,
@@ -170,6 +192,7 @@ class HTTPRequest:
170
192
  Returns:
171
193
  捕获的值字典
172
194
  """
195
+
173
196
  self._ensure_response_exists("捕获响应")
174
197
 
175
198
  captures_config = self.config.get('captures', {})
@@ -181,18 +204,22 @@ class HTTPRequest:
181
204
  # 提取捕获信息
182
205
  try:
183
206
  extractor_type = capture_spec[0]
184
- extraction_path = capture_spec[1] if len(capture_spec) > 1 else None
207
+ extraction_path = capture_spec[1] if len(
208
+ capture_spec) > 1 else None
185
209
 
186
210
  # 检查是否有length参数
187
211
  is_length_capture = False
188
212
  if len(capture_spec) > 2 and capture_spec[2] == "length":
189
213
  is_length_capture = True
190
- default_value = capture_spec[3] if len(capture_spec) > 3 else None
214
+ default_value = capture_spec[3] if len(
215
+ capture_spec) > 3 else None
191
216
  else:
192
- default_value = capture_spec[2] if len(capture_spec) > 2 else None
217
+ default_value = capture_spec[2] if len(
218
+ capture_spec) > 2 else None
193
219
 
194
220
  # 提取值
195
- captured_value = self._extract_value(extractor_type, extraction_path, default_value)
221
+ captured_value = self._extract_value(
222
+ extractor_type, extraction_path, default_value)
196
223
 
197
224
  # 特殊处理length
198
225
  if is_length_capture:
@@ -219,7 +246,8 @@ class HTTPRequest:
219
246
  attachment_type=allure.attachment_type.TEXT
220
247
  )
221
248
  # 抛出更具体的异常,而不是用0替代
222
- raise ValueError(f"断言类型'length'无法应用于值 '{original_value}': {str(e)}")
249
+ raise ValueError(
250
+ f"断言类型'length'无法应用于值 '{original_value}': {str(e)}")
223
251
  else:
224
252
  # 记录捕获到Allure
225
253
  allure.attach(
@@ -246,15 +274,15 @@ class HTTPRequest:
246
274
  return self.captured_values
247
275
 
248
276
  def _format_error_details(self,
249
- extractor_type: str,
250
- extraction_path: str,
251
- assertion_type: str,
252
- compare_operator: str,
253
- actual_value: Any,
254
- expected_value: Any,
255
- original_actual_value: Any = None,
256
- error_message: str = None,
257
- additional_context: str = None) -> str:
277
+ extractor_type: str,
278
+ extraction_path: str,
279
+ assertion_type: str,
280
+ compare_operator: str,
281
+ actual_value: Any,
282
+ expected_value: Any,
283
+ original_actual_value: Any = None,
284
+ error_message: str = None,
285
+ additional_context: str = None) -> str:
258
286
  """格式化断言错误的详细信息
259
287
 
260
288
  Args:
@@ -287,7 +315,7 @@ class HTTPRequest:
287
315
 
288
316
  # 添加断言类型和值信息
289
317
  if assertion_type == "length":
290
- error_details.append(f"断言类型: 长度比较")
318
+ error_details.append("断言类型: 长度比较")
291
319
  if original_actual_value is not None:
292
320
  error_details.append(f"原始值: {original_actual_value}")
293
321
  error_details.append(f"实际长度: {actual_value}")
@@ -324,6 +352,7 @@ class HTTPRequest:
324
352
  Returns:
325
353
  Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]: 断言结果列表和失败需重试的断言列表
326
354
  """
355
+
327
356
  self._ensure_response_exists("进行断言")
328
357
 
329
358
  asserts_config = self.config.get('asserts', [])
@@ -340,15 +369,19 @@ class HTTPRequest:
340
369
  global_retry_enabled = bool(retry_config)
341
370
 
342
371
  # 3. 提取重试默认设置
343
- global_retry_count = retry_assertions_config.get('count', retry_config.get('count', 3))
344
- global_retry_interval = retry_assertions_config.get('interval', retry_config.get('interval', 1))
372
+ global_retry_count = retry_assertions_config.get(
373
+ 'count', retry_config.get('count', 3))
374
+ global_retry_interval = retry_assertions_config.get(
375
+ 'interval', retry_config.get('interval', 1))
345
376
 
346
377
  # 4. 提取应该重试的断言索引列表
347
- retry_all_assertions = retry_assertions_config.get('all', global_retry_enabled)
378
+ retry_all_assertions = retry_assertions_config.get(
379
+ 'all', global_retry_enabled)
348
380
  retry_assertion_indices = retry_assertions_config.get('indices', [])
349
381
 
350
382
  # 5. 提取特定断言的重试配置
351
- specific_assertion_configs = retry_assertions_config.get('specific', {})
383
+ specific_assertion_configs = retry_assertions_config.get(
384
+ 'specific', {})
352
385
 
353
386
  # 如果传入了specific_asserts,只处理指定的断言
354
387
  process_asserts = specific_asserts if specific_asserts is not None else asserts_config
@@ -361,7 +394,8 @@ class HTTPRequest:
361
394
  extractor_type = assertion[0]
362
395
 
363
396
  # 获取原始索引(用于重试配置查找)
364
- original_idx = index_mapping.get(assertion_idx, assertion_idx) if index_mapping else assertion_idx
397
+ original_idx = index_mapping.get(
398
+ assertion_idx, assertion_idx) if index_mapping else assertion_idx
365
399
 
366
400
  # 判断该断言是否应该重试(只使用retry_assertions配置)
367
401
  is_retryable = False
@@ -371,7 +405,8 @@ class HTTPRequest:
371
405
  # retry_assertions特定配置 - 使用原始索引和支持整数键
372
406
  original_idx_str = str(original_idx)
373
407
  if original_idx_str in specific_assertion_configs or original_idx in specific_assertion_configs:
374
- spec_config = specific_assertion_configs.get(original_idx_str) or specific_assertion_configs.get(original_idx)
408
+ spec_config = specific_assertion_configs.get(
409
+ original_idx_str) or specific_assertion_configs.get(original_idx)
375
410
  is_retryable = True
376
411
  if isinstance(spec_config, dict):
377
412
  if 'count' in spec_config:
@@ -395,7 +430,8 @@ class HTTPRequest:
395
430
  # 检查第二个元素是否是操作符或二者兼用的断言类型
396
431
  if assertion[1] in COMPARISON_OPERATORS or assertion[1] in DUAL_PURPOSE:
397
432
  # 这是操作符格式的断言 ["status", "eq", 200]
398
- extraction_path = None if extractor_type in ["status", "body", "response_time"] else assertion[1]
433
+ extraction_path = None if extractor_type in [
434
+ "status", "body", "response_time"] else assertion[1]
399
435
 
400
436
  # 特殊处理二者兼用的断言类型
401
437
  if assertion[1] in DUAL_PURPOSE:
@@ -443,20 +479,23 @@ class HTTPRequest:
443
479
  else:
444
480
  # 检查断言类型是否有效
445
481
  if assertion_type not in ASSERTION_TYPES:
446
- raise ValueError(f"不支持的断言类型: {assertion_type} 在 {assertion}")
482
+ raise ValueError(
483
+ f"不支持的断言类型: {assertion_type} 在 {assertion}")
447
484
 
448
485
  # 检查此断言类型是否需要预期值
449
486
  if assertion_type not in ["exists", "not_exists"]:
450
487
  # 特殊处理类型断言,它确实需要一个值但在这种格式中没有提供
451
488
  if assertion_type == "type":
452
- raise ValueError(f"断言类型 'type' 需要预期值(string/number/boolean/array/object/null),但未提供: {assertion}")
489
+ raise ValueError(
490
+ f"断言类型 'type' 需要预期值(string/number/boolean/array/object/null),但未提供: {assertion}")
453
491
  # 断言类型作为操作符
454
492
  expected_value = None
455
493
  compare_operator = assertion_type # 使用断言类型作为操作符
456
494
  else:
457
495
  expected_value = None
458
496
  compare_operator = assertion_type # 存在性断言的操作符就是断言类型本身
459
- elif len(assertion) == 4: # 带操作符的断言 ["jsonpath", "$.id", "eq", 1] 或特殊断言 ["jsonpath", "$.type", "type", "string"] 或长度断言 ["body", "length", "gt", 50]
497
+ # 带操作符的断言 ["jsonpath", "$.id", "eq", 1] 或特殊断言 ["jsonpath", "$.type", "type", "string"] 或长度断言 ["body", "length", "gt", 50]
498
+ elif len(assertion) == 4:
460
499
  extraction_path = assertion[1]
461
500
 
462
501
  # 特殊处理长度断言:["body", "length", "gt", 50]
@@ -475,7 +514,8 @@ class HTTPRequest:
475
514
  else:
476
515
  # 检查断言类型是否有效
477
516
  if assertion[2] not in ASSERTION_TYPES:
478
- raise ValueError(f"不支持的断言类型: {assertion[2]} 在 {assertion}")
517
+ raise ValueError(
518
+ f"不支持的断言类型: {assertion[2]} 在 {assertion}")
479
519
 
480
520
  # 其他类型的断言,比如特殊断言
481
521
  assertion_type = assertion[2]
@@ -495,13 +535,15 @@ class HTTPRequest:
495
535
 
496
536
  # 检查断言类型是否有效
497
537
  if assertion_type not in ASSERTION_TYPES:
498
- raise ValueError(f"不支持的断言类型: {assertion_type} 在 {assertion}")
538
+ raise ValueError(
539
+ f"不支持的断言类型: {assertion_type} 在 {assertion}")
499
540
 
500
541
  # 特殊处理长度断言
501
542
  if assertion_type == "length":
502
543
  # 验证第四个元素是有效的比较操作符
503
544
  if assertion[3] not in COMPARISON_OPERATORS:
504
- raise ValueError(f"长度断言的比较操作符必须是 {', '.join(COMPARISON_OPERATORS)} 之一: {assertion}")
545
+ raise ValueError(
546
+ f"长度断言的比较操作符必须是 {', '.join(COMPARISON_OPERATORS)} 之一: {assertion}")
505
547
 
506
548
  # 验证第五个元素是有效的长度值
507
549
  try:
@@ -557,7 +599,8 @@ class HTTPRequest:
557
599
  attachment_type=allure.attachment_type.TEXT
558
600
  )
559
601
  # 抛出更具体的异常,而不是用0替代
560
- raise ValueError(f"断言类型'length'无法应用于值 '{original_actual_value}': {str(e)}")
602
+ raise ValueError(
603
+ f"断言类型'length'无法应用于值 '{original_actual_value}': {str(e)}")
561
604
 
562
605
  # 执行断言
563
606
  assertion_result = {
@@ -576,7 +619,8 @@ class HTTPRequest:
576
619
 
577
620
  try:
578
621
  # 验证断言
579
- result = self._perform_assertion(assertion_type, compare_operator, actual_value, expected_value)
622
+ result = self._perform_assertion(
623
+ assertion_type, compare_operator, actual_value, expected_value)
580
624
  assertion_result['result'] = result
581
625
 
582
626
  # 根据返回的布尔值确定断言是否通过
@@ -596,7 +640,8 @@ class HTTPRequest:
596
640
 
597
641
  # 使用Allure记录断言失败
598
642
  allure.attach(
599
- self._format_assertion_details(assertion_result) + "\n\n错误: 断言结果为False",
643
+ self._format_assertion_details(
644
+ assertion_result) + "\n\n错误: 断言结果为False",
600
645
  name=f"断言失败: {extractor_type}",
601
646
  attachment_type=allure.attachment_type.TEXT
602
647
  )
@@ -643,7 +688,8 @@ class HTTPRequest:
643
688
 
644
689
  # 使用Allure记录断言失败
645
690
  allure.attach(
646
- self._format_assertion_details(assertion_result) + f"\n\n错误: {str(e)}",
691
+ self._format_assertion_details(
692
+ assertion_result) + f"\n\n错误: {str(e)}",
647
693
  name=f"断言失败: {extractor_type}",
648
694
  attachment_type=allure.attachment_type.TEXT
649
695
  )
@@ -676,7 +722,8 @@ class HTTPRequest:
676
722
 
677
723
  # 使用Allure记录断言错误
678
724
  allure.attach(
679
- self._format_assertion_details(assertion_result) + f"\n\n错误: {assertion_result['error']}",
725
+ self._format_assertion_details(
726
+ assertion_result) + f"\n\n错误: {assertion_result['error']}",
680
727
  name=f"断言执行错误: {extractor_type}",
681
728
  attachment_type=allure.attachment_type.TEXT
682
729
  )
@@ -689,7 +736,8 @@ class HTTPRequest:
689
736
  # 执行完所有断言后,如果有失败的断言,抛出异常
690
737
  if failed_assertions:
691
738
  # 检查是否只收集失败而不抛出异常(由重试机制设置)
692
- collect_only = self.config.get('_collect_failed_assertions_only', False)
739
+ collect_only = self.config.get(
740
+ '_collect_failed_assertions_only', False)
693
741
 
694
742
  if not collect_only:
695
743
  if len(failed_assertions) == 1:
@@ -702,7 +750,8 @@ class HTTPRequest:
702
750
  # 从错误消息中提取关键部分
703
751
  if "[" in error_msg and "]" in error_msg:
704
752
  # 尝试提取提取器类型
705
- extractor_type = error_msg.split("[", 1)[1].split("]")[0] if "[" in error_msg else "未知"
753
+ extractor_type = error_msg.split("[", 1)[1].split("]")[
754
+ 0] if "[" in error_msg else "未知"
706
755
  else:
707
756
  extractor_type = "未知"
708
757
 
@@ -759,7 +808,7 @@ class HTTPRequest:
759
808
  Returns:
760
809
  提取的值
761
810
  """
762
- if not self.response:
811
+ if self.response is None:
763
812
  return default_value
764
813
 
765
814
  try:
@@ -868,14 +917,33 @@ class HTTPRequest:
868
917
  else:
869
918
  text = self.response.text
870
919
 
871
- matches = re.findall(pattern, text)
920
+ # 检查正则表达式是否包含捕获组
921
+ compiled_pattern = re.compile(pattern)
922
+ has_groups = compiled_pattern.groups > 0
872
923
 
873
- if not matches:
874
- return default_value
875
- elif len(matches) == 1:
876
- return matches[0]
924
+ if has_groups:
925
+ # 如果有捕获组,只返回第一个匹配的捕获组内容
926
+ first_match = re.search(pattern, text)
927
+ if not first_match:
928
+ return default_value
929
+
930
+ # 获取第一个匹配的捕获组
931
+ if compiled_pattern.groups == 1:
932
+ # 只有一个捕获组,返回字符串
933
+ return first_match.group(1)
934
+ else:
935
+ # 多个捕获组,返回元组
936
+ return first_match.groups()
877
937
  else:
878
- return matches
938
+ # 如果没有捕获组,使用findall获取所有完整匹配
939
+ matches = re.findall(pattern, text)
940
+
941
+ if not matches:
942
+ return default_value
943
+ elif len(matches) == 1:
944
+ return matches[0]
945
+ else:
946
+ return matches
879
947
  except Exception as e:
880
948
  if default_value is not None:
881
949
  return default_value
@@ -1013,7 +1081,8 @@ class HTTPRequest:
1013
1081
  return isinstance(actual_value, str) and actual_value.endswith(str(expected_value))
1014
1082
  elif assertion_type == "matches":
1015
1083
  if not isinstance(actual_value, str):
1016
- actual_value = str(actual_value) if actual_value is not None else ""
1084
+ actual_value = str(
1085
+ actual_value) if actual_value is not None else ""
1017
1086
  try:
1018
1087
  import re
1019
1088
  pattern = str(expected_value)
@@ -1124,17 +1193,20 @@ class HTTPRequest:
1124
1193
  elif operator == "startswith":
1125
1194
  # startswith操作符检查actual是否以expected开头
1126
1195
  if not isinstance(actual_value, str):
1127
- actual_value = str(actual_value) if actual_value is not None else ""
1196
+ actual_value = str(
1197
+ actual_value) if actual_value is not None else ""
1128
1198
  return actual_value.startswith(str(expected_value))
1129
1199
  elif operator == "endswith":
1130
1200
  # endswith操作符检查actual是否以expected结尾
1131
1201
  if not isinstance(actual_value, str):
1132
- actual_value = str(actual_value) if actual_value is not None else ""
1202
+ actual_value = str(
1203
+ actual_value) if actual_value is not None else ""
1133
1204
  return actual_value.endswith(str(expected_value))
1134
1205
  elif operator == "matches":
1135
1206
  # matches操作符使用正则表达式进行匹配
1136
1207
  if not isinstance(actual_value, str):
1137
- actual_value = str(actual_value) if actual_value is not None else ""
1208
+ actual_value = str(
1209
+ actual_value) if actual_value is not None else ""
1138
1210
  try:
1139
1211
  import re
1140
1212
  pattern = str(expected_value)
@@ -1198,7 +1270,8 @@ class HTTPRequest:
1198
1270
  if "json" in request_kwargs and request_kwargs["json"]:
1199
1271
  request_details.append("JSON Body:")
1200
1272
  try:
1201
- request_details.append(json.dumps(request_kwargs["json"], indent=2, ensure_ascii=False))
1273
+ request_details.append(json.dumps(
1274
+ request_kwargs["json"], indent=2, ensure_ascii=False))
1202
1275
  except:
1203
1276
  request_details.append(str(request_kwargs["json"]))
1204
1277
  elif "data" in request_kwargs and request_kwargs["data"]:
@@ -1243,7 +1316,8 @@ class HTTPRequest:
1243
1316
  response_details.append("Body:")
1244
1317
  try:
1245
1318
  if 'application/json' in response.headers.get('Content-Type', ''):
1246
- response_details.append(json.dumps(response.json(), indent=2, ensure_ascii=False))
1319
+ response_details.append(json.dumps(
1320
+ response.json(), indent=2, ensure_ascii=False))
1247
1321
  elif len(response.content) < 10240: # 限制大小
1248
1322
  response_details.append(response.text)
1249
1323
  else:
@@ -1256,4 +1330,4 @@ class HTTPRequest:
1256
1330
  "\n".join(response_details),
1257
1331
  name=f"HTTP响应: {response_summary}",
1258
1332
  attachment_type=allure.attachment_type.TEXT
1259
- )
1333
+ )
@@ -110,7 +110,7 @@
110
110
  asserts:
111
111
  - ["status", "eq", 200]
112
112
  - ["jsonpath", "$.args.task_id", "eq", "${task_id}"] # 这个断言一定会通过
113
- - ["response_time", "lt", 1000] # 这个断言可能失败,因为延迟是2
113
+ - ["response_time", "lt", 4000] # 2秒延迟留出足够的缓冲时间
114
114
  ''',断言重试次数: 3,断言重试间隔: 1
115
115
 
116
116
  [打印],内容:'任务查询完成,URL: ${url}'
@@ -16,7 +16,7 @@
16
16
  asserts:
17
17
  - ["status", "eq", 200]
18
18
  - ["jsonpath", "$.url", "contains", "httpbin.org"]
19
- - ["response_time", "lt", 3000] # 这个断言可能会失败,因为请求延迟约2
19
+ - ["response_time", "lt", 4000] # 2秒延迟留出足够的缓冲时间
20
20
  ''',步骤名称: "旧版全局断言重试示例"
21
21
 
22
22
  # 示例2: 使用indices指定重试特定断言
@@ -38,7 +38,7 @@
38
38
  asserts:
39
39
  - ["status", "eq", 200] # 索引0
40
40
  - ["body", "matches", "^[0-9]+$"] # 索引1
41
- - ["body", "lt", 6] # 索引2 - 需要重试
41
+ - ["body", "lt", 6] # 索引2 - 需要重试,约50%概率失败
42
42
  - ["response_time", "lt", 10000] # 索引3
43
43
  retry_assertions:
44
44
  count: 5
@@ -56,7 +56,7 @@
56
56
  url: https://httpbin.org/delay/1
57
57
  asserts:
58
58
  - ["status", "eq", 200] # 索引0
59
- - ["response_time", "lt", 2000] # 索引1
59
+ - ["response_time", "lt", 3000] # 索引1 - 给1秒延迟留出缓冲
60
60
  - ["jsonpath", "$.headers.Host", "eq", "httpbin.org"] # 索引2
61
61
  - ["jsonpath", "$.url", "contains", "httpbin.org"] # 索引3
62
62
  retry_assertions:
@@ -79,7 +79,7 @@
79
79
  url: https://httpbin.org/status/200
80
80
  asserts:
81
81
  - ["status", "eq", 200]
82
- - ["response_time", "lt", 100] # 可能会失败
82
+ - ["response_time", "lt", 5000] # 设置合理的响应时间限制
83
83
  retry_assertions:
84
84
  count: 3
85
85
  interval: 1.5
@@ -13,7 +13,7 @@
13
13
  asserts:
14
14
  - ["status", "eq", 200]
15
15
  - ["jsonpath", "$.url", "contains", "httpbin.org"]
16
- - ["response_time", "lt", 3003] # 这个断言可能会失败,因为请求延迟约2
16
+ - ["response_time", "lt", 4000] # 2秒延迟留出足够的缓冲时间
17
17
  retry_assertions:
18
18
  count: 3
19
19
  interval: 1
@@ -57,7 +57,7 @@
57
57
  url: https://httpbin.org/delay/1
58
58
  asserts:
59
59
  - ["status", "eq", 200] # 索引0
60
- - ["response_time", "lt", 2500] # 索引1
60
+ - ["response_time", "lt", 3000] # 索引1 - 给1秒延迟留出缓冲
61
61
  - ["jsonpath", "$.headers.Host", "eq", "httpbin.org"] # 索引2
62
62
  retry_assertions:
63
63
  # 全局设置
@@ -12,7 +12,7 @@
12
12
  url: https://httpbin.org/delay/1
13
13
  asserts:
14
14
  - ["status", "eq", 200]
15
- - ["response_time", "lt", 100] # 强制失败,设置为很小的值
15
+ - ["response_time", "lt", 3000] # 给1秒延迟留出合理的缓冲时间
16
16
  retry_assertions:
17
17
  count: 2
18
18
  interval: 1
@@ -10,7 +10,7 @@
10
10
  url: https://httpbin.org/delay/1
11
11
  asserts:
12
12
  - ["status", "eq", 200]
13
- - ["response_time", "lt", 500] # 可能会失败,因为请求延迟约1
13
+ - ["response_time", "lt", 3000] # 1秒延迟留出合理的缓冲时间
14
14
  # 全局重试配置
15
15
  retry_assertions:
16
16
  count: 2
@@ -24,7 +24,7 @@
24
24
  url: https://httpbin.org/delay/1
25
25
  asserts:
26
26
  - ["status", "eq", 200] # 索引0
27
- - ["response_time", "lt", 500] # 索引1,需要重试
27
+ - ["response_time", "lt", 3000] # 索引1,给1秒延迟留出缓冲
28
28
  # 只重试指定索引的断言
29
29
  retry_assertions:
30
30
  count: 3
@@ -38,7 +38,7 @@
38
38
  url: https://httpbin.org/delay/1
39
39
  asserts:
40
40
  - ["status", "eq", 200] # 索引0
41
- - ["response_time", "lt", 500] # 索引1,使用特定配置
41
+ - ["response_time", "lt", 3000] # 索引1,给1秒延迟留出缓冲
42
42
  - ["body", "contains", "url"] # 索引2
43
43
  # 为特定断言提供自定义配置
44
44
  retry_assertions:
@@ -10,7 +10,7 @@
10
10
  url: https://httpbin.org/delay/1
11
11
  asserts:
12
12
  - ["status", "eq", 200]
13
- - ["response_time", "lt", 500]
13
+ - ["response_time", "lt", 3000]
14
14
  retry_assertions:
15
15
  count: 2
16
16
  interval: 1
@@ -23,7 +23,7 @@
23
23
  url: https://httpbin.org/delay/1
24
24
  asserts:
25
25
  - ["status", "eq", 200]
26
- - ["response_time", "lt", 500]
26
+ - ["response_time", "lt", 3000]
27
27
  retry_assertions:
28
28
  count: 3
29
29
  interval: 0.5
@@ -36,7 +36,7 @@
36
36
  url: https://httpbin.org/delay/1
37
37
  asserts:
38
38
  - ["status", "eq", 200]
39
- - ["response_time", "lt", 500]
39
+ - ["response_time", "lt", 3000]
40
40
  - ["body", "contains", "url"]
41
41
  retry_assertions:
42
42
  specific:
@@ -1,22 +1,33 @@
1
1
  @name: 断言重试调试示例
2
- @description: 使用更严格的断言条件测试断言重试功能
2
+ @description: 使用随机数断言测试断言重试功能
3
3
  @tags: [HTTP, API, 断言重试, 调试]
4
4
  @author: Felix
5
5
  @date: 2024-06-15
6
6
 
7
- [打印],内容:'开始测试断言重试功能 - 故意设置极低的响应时间限制'
7
+ [打印],内容:'开始测试断言重试功能 - 使用随机数模拟不确定结果'
8
8
 
9
- # 使用非常低的响应时间限制确保断言失败
9
+ # 使用随机数断言来模拟可能失败的断言,这比响应时间更可靠
10
10
  [HTTP请求],客户端:'default',配置:'''
11
11
  method: GET
12
- url: https://httpbin.org/delay/1
12
+ url: https://www.random.org/integers/
13
+ request:
14
+ params:
15
+ num: 1
16
+ min: 1
17
+ max: 10
18
+ col: 1
19
+ base: 10
20
+ format: plain
21
+ captures:
22
+ random_number: ["body"]
13
23
  asserts:
14
24
  - ["status", "eq", 200] # 这个会通过
15
- - ["response_time", "lt", 50] # 设置极低的值确保失败,1秒的延迟应该会超过50ms
25
+ - ["body", "lt", 5] # 约40%概率失败,用于测试重试逻辑
16
26
  retry_assertions:
17
- count: 2
18
- interval: 0.5
27
+ count: 3
28
+ interval: 1
19
29
  all: true
20
- ''',步骤名称:"故意触发断言失败的示例"
30
+ ''',步骤名称:"使用随机数测试重试功能"
21
31
 
32
+ [打印],内容:'获取的随机数: ${random_number}'
22
33
  [打印],内容:'测试完成 - 如果执行到这里,说明重试逻辑正常工作'
@@ -10,7 +10,7 @@
10
10
  url: https://httpbin.org/delay/1
11
11
  asserts:
12
12
  - ["status", "eq", 200]
13
- - ["response_time", "lt", 1000]
13
+ - ["response_time", "lt", 3000] # 给1秒延迟留出合理的缓冲时间
14
14
  # 全局重试配置
15
15
  retry_assertions:
16
16
  count: 2