Skip to content

Latest commit

 

History

History
 
 

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

代码阅读指南(python 部分)

核心数据结构

HttpRunner 以 TestCase 为核心,将任意测试场景抽象为有序步骤的集合。

class TestCase(BaseModel):
    config: TConfig
    teststeps: List[TStep]

针对每种测试步骤,统一继承自 IStep,并要求必须至少实现如下 4 个方法;步骤内容统一在 run 方法中进行实现。

class IStep(object):

    def name(self) -> str:
        raise NotImplementedError

    def type(self) -> str:
        raise NotImplementedError

    def struct(self) -> TStep:
        raise NotImplementedError

    def run(self, runner) -> StepData:
        # runner: HttpRunner
        raise NotImplementedError

我们只需遵循 IStep 的接口定义,即可实现各种类型的测试步骤类型。当前 python 版本已支持的步骤类型包括:

  • request:发起单次 HTTP 请求
  • testcase:引用执行其它测试用例文件

基于该机制,我们可以扩展支持新的协议类型,例如 HTTP2/WebSocket/RPC 等;同时也可以支持新的测试类型,例如 UI 自动化。甚至我们还可以在一个测试用例中混合调用多种不同的 Step 类型,例如实现 HTTP/RPC/UI 混合场景。

用例编写

运行主流程

整体控制器 pytest

不同于 golang 版本,python 版本的控制逻辑都基于 pytest 的用例发现和执行机制。

  • 如果是运行 JSON/YAML 格式的用例,hrp 会将用例转换为 pytest 支持的用例格式
  • 如果是要自行编写 pytest 测试用例,需要遵循 HttpRunner 的格式要求

pytest 用例格式要求

所有测试用例要求都继承自 HttpRunner,然后

结构如下所示:

class TestCaseRequestWithFunctions(HttpRunner):

    config = (
        Config("request methods testcase with functions")
    )

    teststeps = [
        Step(
            RunRequest("get with params")...
        ),
        Step(
            RunRequest("post raw text")...
        ),
        Step(
            RunRequest("post form data")...
        ),
    ]

完整案例可参考:

用例执行器 SessionRunner

测试用例的具体执行都由 SessionRunner 完成,每个 TestCase 对应一个实例,在该实例中除了包含测试用例自身内容外,还会包含测试过程的 session 数据和最终测试结果 summary。

class SessionRunner(object):
	config: Config
    teststeps: List[object]     # list of Step
    ...

重点关注一个方法:

  • test_start:该方法将被 pytest 发现,作为启动执行入口,依次执行所有测试步骤
def test_start(self, param: Dict = None) -> "SessionRunner":
    """main entrance, discovered by pytest"""
    self.__start_at = time.time()
    try:
        # run step in sequential order
        for step in self.teststeps:
            self.__run_step(step)
    finally:
        logger.info(f"generate testcase log: {self.__log_path}")

    self.__duration = time.time() - self.__start_at

在主流程中,SessionRunner 并不需要关注 step 的具体类型,统一都是调用 step.run(self),具体实现逻辑都在对应 step 的 run 方法中。

def run(self, runner: HttpRunner) -> StepData:
    return self.__step.run(runner)