mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-07 08:12:41 +08:00
feat: support retry when test step failed
This commit is contained in:
@@ -20,6 +20,8 @@
|
||||
- change: remove har2case, move all features to go version, replace with `hrp run`
|
||||
- change: remove locust, you should run load tests with go version, replace with `hrp boom`
|
||||
- change: remove fastapi and uvicorn dependencies
|
||||
- change: add pytest.ini to make log colorful
|
||||
- feat: support retry when test step failed
|
||||
- fix: ignore exceptions when reporting GA events
|
||||
- fix: remove misuse of NoReturn in Python typing
|
||||
|
||||
|
||||
@@ -23,3 +23,20 @@ def get_app_version():
|
||||
|
||||
def calculate_two_nums(a, b=1):
|
||||
return [a + b, b - a]
|
||||
|
||||
|
||||
def fake_rand_count():
|
||||
"""
|
||||
return 1 at first call
|
||||
return 2 at second call
|
||||
"""
|
||||
l = []
|
||||
|
||||
def func():
|
||||
l.append(1)
|
||||
return len(l)
|
||||
|
||||
return func
|
||||
|
||||
|
||||
fake_randnum = fake_rand_count()
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
|
||||
@Date : 2022/4/7
|
||||
@File : request_with_retry.py
|
||||
@Author : duanchao.bill
|
||||
@Desc :
|
||||
|
||||
"""
|
||||
|
||||
from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase
|
||||
|
||||
|
||||
class TestCaseRetry(HttpRunner):
|
||||
config = (
|
||||
Config("request methods testcase in hardcode")
|
||||
.base_url("https://postman-echo.com")
|
||||
.verify(False)
|
||||
)
|
||||
|
||||
teststeps = [
|
||||
Step(
|
||||
RunRequest("run with retry")
|
||||
.with_retry(retry_times=1, retry_interval=1)
|
||||
.get("/get")
|
||||
.with_params(**{"foo1": "${fake_randnum()}"})
|
||||
.with_headers(**{"User-Agent": "HttpRunner/3.0"})
|
||||
.validate()
|
||||
.assert_equal("body.args.foo1", "2")
|
||||
)
|
||||
]
|
||||
6
examples/pytest.ini
Normal file
6
examples/pytest.ini
Normal file
@@ -0,0 +1,6 @@
|
||||
[pytest]
|
||||
addopts = -s
|
||||
# https://docs.pytest.org/en/latest/how-to/output.html
|
||||
junit_logging = all
|
||||
junit_duration_report = total
|
||||
log_cli = False
|
||||
@@ -82,6 +82,9 @@ class TStep(BaseModel):
|
||||
export: Export = []
|
||||
validators: Validators = Field([], alias="validate")
|
||||
validate_script: List[Text] = []
|
||||
retry_times: int = 0
|
||||
retry_interval: int = 0 # sec
|
||||
|
||||
|
||||
|
||||
class TestCase(BaseModel):
|
||||
|
||||
@@ -12,10 +12,9 @@ except ModuleNotFoundError:
|
||||
USE_ALLURE = False
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from httprunner.client import HttpSession
|
||||
from httprunner.config import Config
|
||||
from httprunner.exceptions import ParamsError
|
||||
from httprunner.exceptions import ParamsError, ValidationFailure
|
||||
from httprunner.loader import load_project_meta
|
||||
from httprunner.models import (ProjectMeta, StepResult, TConfig, TestCaseInOut,
|
||||
TestCaseSummary, TestCaseTime, VariablesMapping)
|
||||
@@ -25,7 +24,7 @@ from httprunner.utils import merge_variables
|
||||
|
||||
class SessionRunner(object):
|
||||
config: Config
|
||||
teststeps: List[object] # list of Step
|
||||
teststeps: List[object] # list of Step
|
||||
|
||||
parser: Parser = None
|
||||
session: HttpSession = None
|
||||
@@ -162,11 +161,23 @@ class SessionRunner(object):
|
||||
logger.info(f"run step begin: {step.name()} >>>>>>")
|
||||
|
||||
# run step
|
||||
if USE_ALLURE:
|
||||
with allure.step(f"step: {step.name()}"):
|
||||
step_result: StepResult = step.run(self)
|
||||
else:
|
||||
step_result: StepResult = step.run(self)
|
||||
for i in range(step.retry_times + 1):
|
||||
try:
|
||||
if USE_ALLURE:
|
||||
with allure.step(f"step: {step.name()}"):
|
||||
step_result: StepResult = step.run(self)
|
||||
else:
|
||||
step_result: StepResult = step.run(self)
|
||||
break
|
||||
except ValidationFailure:
|
||||
if i == step.retry_times:
|
||||
raise
|
||||
else:
|
||||
logger.warning(
|
||||
f"run step {step.name()} validation failed,wait {step.retry_interval} sec and try again")
|
||||
time.sleep(step.retry_interval)
|
||||
logger.info(
|
||||
f"run step retry ({i+1}/{step.retry_times} time): {step.name()} >>>>>>")
|
||||
|
||||
# save extracted variables to session variables
|
||||
self.__session_variables.update(step_result.export_vars)
|
||||
|
||||
@@ -27,6 +27,14 @@ class Step(object):
|
||||
def testcase(self) -> TestCase:
|
||||
return self.__step.struct().testcase
|
||||
|
||||
@property
|
||||
def retry_times(self) -> int:
|
||||
return self.__step.struct().retry_times
|
||||
|
||||
@property
|
||||
def retry_interval(self) -> int:
|
||||
return self.__step.struct().retry_interval
|
||||
|
||||
def struct(self) -> TStep:
|
||||
return self.__step.struct()
|
||||
|
||||
|
||||
@@ -426,6 +426,11 @@ class RunRequest(object):
|
||||
self.__step.variables.update(variables)
|
||||
return self
|
||||
|
||||
def with_retry(self, retry_times, retry_interval) -> "RunRequest":
|
||||
self.__step.retry_times = retry_times
|
||||
self.__step.retry_interval = retry_interval
|
||||
return self
|
||||
|
||||
def setup_hook(self, hook: Text, assign_var_name: Text = None) -> "RunRequest":
|
||||
if assign_var_name:
|
||||
self.__step.setup_hooks.append({assign_var_name: hook})
|
||||
|
||||
@@ -80,6 +80,11 @@ class RunTestCase(object):
|
||||
self.__step.variables.update(variables)
|
||||
return self
|
||||
|
||||
def with_retry(self, retry_times, retry_interval) -> "RunRequest":
|
||||
self.__step.retry_times = retry_times
|
||||
self.__step.retry_interval = retry_interval
|
||||
return self
|
||||
|
||||
def setup_hook(self, hook: Text, assign_var_name: Text = None) -> "RunTestCase":
|
||||
if assign_var_name:
|
||||
self.__step.setup_hooks.append({assign_var_name: hook})
|
||||
|
||||
Reference in New Issue
Block a user