feat: support retry when test step failed

This commit is contained in:
duanchao.bill
2022-04-06 20:53:32 +08:00
committed by debugtalk
parent 4466e9b980
commit 4152a6ae80
9 changed files with 96 additions and 8 deletions

View File

@@ -20,6 +20,8 @@
- change: remove har2case, move all features to go version, replace with `hrp run` - 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 locust, you should run load tests with go version, replace with `hrp boom`
- change: remove fastapi and uvicorn dependencies - 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: ignore exceptions when reporting GA events
- fix: remove misuse of NoReturn in Python typing - fix: remove misuse of NoReturn in Python typing

View File

@@ -23,3 +23,20 @@ def get_app_version():
def calculate_two_nums(a, b=1): def calculate_two_nums(a, b=1):
return [a + b, b - a] 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()

View File

@@ -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
View 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

View File

@@ -82,6 +82,9 @@ class TStep(BaseModel):
export: Export = [] export: Export = []
validators: Validators = Field([], alias="validate") validators: Validators = Field([], alias="validate")
validate_script: List[Text] = [] validate_script: List[Text] = []
retry_times: int = 0
retry_interval: int = 0 # sec
class TestCase(BaseModel): class TestCase(BaseModel):

View File

@@ -12,10 +12,9 @@ except ModuleNotFoundError:
USE_ALLURE = False USE_ALLURE = False
from loguru import logger from loguru import logger
from httprunner.client import HttpSession from httprunner.client import HttpSession
from httprunner.config import Config 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.loader import load_project_meta
from httprunner.models import (ProjectMeta, StepResult, TConfig, TestCaseInOut, from httprunner.models import (ProjectMeta, StepResult, TConfig, TestCaseInOut,
TestCaseSummary, TestCaseTime, VariablesMapping) TestCaseSummary, TestCaseTime, VariablesMapping)
@@ -25,7 +24,7 @@ from httprunner.utils import merge_variables
class SessionRunner(object): class SessionRunner(object):
config: Config config: Config
teststeps: List[object] # list of Step teststeps: List[object] # list of Step
parser: Parser = None parser: Parser = None
session: HttpSession = None session: HttpSession = None
@@ -162,11 +161,23 @@ class SessionRunner(object):
logger.info(f"run step begin: {step.name()} >>>>>>") logger.info(f"run step begin: {step.name()} >>>>>>")
# run step # run step
if USE_ALLURE: for i in range(step.retry_times + 1):
with allure.step(f"step: {step.name()}"): try:
step_result: StepResult = step.run(self) if USE_ALLURE:
else: with allure.step(f"step: {step.name()}"):
step_result: StepResult = step.run(self) 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 # save extracted variables to session variables
self.__session_variables.update(step_result.export_vars) self.__session_variables.update(step_result.export_vars)

View File

@@ -27,6 +27,14 @@ class Step(object):
def testcase(self) -> TestCase: def testcase(self) -> TestCase:
return self.__step.struct().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: def struct(self) -> TStep:
return self.__step.struct() return self.__step.struct()

View File

@@ -426,6 +426,11 @@ class RunRequest(object):
self.__step.variables.update(variables) self.__step.variables.update(variables)
return self 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": def setup_hook(self, hook: Text, assign_var_name: Text = None) -> "RunRequest":
if assign_var_name: if assign_var_name:
self.__step.setup_hooks.append({assign_var_name: hook}) self.__step.setup_hooks.append({assign_var_name: hook})

View File

@@ -80,6 +80,11 @@ class RunTestCase(object):
self.__step.variables.update(variables) self.__step.variables.update(variables)
return self 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": def setup_hook(self, hook: Text, assign_var_name: Text = None) -> "RunTestCase":
if assign_var_name: if assign_var_name:
self.__step.setup_hooks.append({assign_var_name: hook}) self.__step.setup_hooks.append({assign_var_name: hook})