Flask+TestRunner 自动化测试系统实现
实现了一个前端可以执行测试用例,测试过程查询的一个自动化测试系统,实现效果如下 大佬们 可以评论私信交流代码、思路


一、Flask与TestRunner的协同合作逻辑
Flask(Web管控层)与TestRunner(自动化执行层)围绕"接口传指令、指令生配置、状态互同步、线程隔干扰"构建通用化"管控-执行"闭环,不绑定具体业务场景,可适配各类自动化测试需求,具体协同流程与优势如下:
1. 协同流程(通用化时间线)
- 需求触发:前端测试平台/CI/CD工具(如Jenkins)通过Flask的标准化接口,发起测试请求,执行特定用例
- 配置转化:包含两个部分,维护测试床(环境信息):"测试环境参数(如域名、账号)、通用配置(如超时时间)",组装成TestRunner可识别的结构化配置;测试套(测试用例),接口触发特定条件下的测试用例。
- 任务托管 :Flask通过任务管理器(如
TestTaskManager
)生成唯一任务ID,启动独立守护线程,将配置传递给TestRunner并触发测试执行; - 执行同步:TestRunner执行过程中,实时记录用例、步骤的状态(如"进行中""通过");Flask通过绑定的TestRunner实例,动态读取执行数据,更新任务进度(如"50%");
- 结果反馈:TestRunner执行完成后,Flask收集"用例详情、错误日志、耗时"等结果,通过状态查询接口返回给前端,日志留痕,形成闭环
2. 协同核心优势
- 职责解耦易扩展:Flask专注"对外交互+任务管控",TestRunner专注"自动化执行",两者代码独立------例如Flask新增"结果导出"接口,TestRunner优化执行速度,互不影响;
- 多任务并发无阻塞:每个TestRunner实例运行在独立线程,Flask主线程可同时处理多个测试请求,不会因单个任务未完成导致整体卡住;
- 结果追溯可定位:通过唯一任务ID关联"请求参数→执行过程→结果数据",即使多任务并行,也能快速定位某条用例失败原因。
二、Flask:自动化测试的"对外桥梁"与"任务中枢"
Flask作为Web框架,核心作用是为TestRunner提供"外部交互入口 "和"任务全生命周期管控",屏蔽TestRunner的技术细节,方便测试人员测试的同时,也能让让非技术人员/外部工具也能便捷使用自动化测试能力。
1. 核心功能(通用化能力)
(1)提供标准化测试触发接口
- 功能说明:开发通用POST接口,接收外部测试请求,支持传递"业务类型、环境标识、自定义参数",并对请求合法性进行校验(如参数是否完整、业务类型是否支持); 1、将测试用例目录下的测试用例全部查询出来,通过路径执行对应测试用例(可增加用例分组) 2、将核心功能测试用例放到单一页面直接执行,可参数化执行核心功能
- 核心逻辑:外部请求→参数校验→匹配用例列表→组装TestRunner配置→触发执行;
- 通用价值:解决TestRunner"无法主动接收外部请求"的问题------无论是测试人员通过前端点击按钮,还是CI/CD流水线自动触发,都能通过HTTP接口发起测试,降低使用门槛。同时还可以加入定时任务执行,接入外部办公api,实现长稳测试+监控。
(2)实现任务统一管理
- 功能说明 :通过单例任务管理器(如
TestTaskManager
),实现"任务创建、状态跟踪、过期清理":生成唯一任务ID标识每个测试任务,用任务对象(如TestTask
)封装"执行状态、进度、绑定的TestRunner实例" - 核心逻辑:创建任务→绑定执行引擎→实时更新状态→查询反馈→导入测试报告;
- 通用价值:避免TestRunner"无管控执行"------测试人员可随时查询任务进度(如"执行到第3步,共5步"),不用等待测试完全结束才知道结果。
(3)保障多任务执行隔离
- 功能说明:为每个TestRunner实例分配独立守护线程,确保不同任务的执行环境互不干扰(如A任务的接口请求异常不会影响B任务);(限制并发任务数量,避免资源耗尽;)
- 核心逻辑:接收请求→判断并发数→启动独立线程→执行TestRunner→任务结束回收线程;
- 通用价值:支持多业务线同时发起测试,且系统稳定性不受影响。
2. 核心价值总结
Flask的核心作用是"让TestRunner的能力可被外部调用、可被管控"------通过标准化接口降低使用门槛,通过任务管理提升可控性,通过线程隔离保障稳定性,是TestRunner与外部系统交互的"中间件"。
三、TestRunner:自动化测试的"执行引擎"
TestRunner作为自动化测试核心执行框架,专注于"接收Flask的配置指令,完成测试用例的加载、执行、结果统计",不关注外部交互,仅聚焦自动化测试本身的标准化流程。
TestRunner的实现就是测试过程
- 测试床:先告诉它再哪个环境执行
- 测试套:执行哪些用例
- 测试数据,执行步骤,是否符合预期
- 测试报告
1. 核心功能实现(通用化执行能力)
先写Base基类,测试用例需要继承它。然后在TestRunner中,实例化测试用例,测试用例继承了Base,所以就可以通过把实例化的测试用例,按照先后顺序,执行方法,比如先执行setUp,都是在TestRunner实例化测试用例之后实现的。本质上是继承,实例化,执行方法。
(1)动态加载测试用例
- 功能说明 :接收Flask传递的"用例路径配置",通过Python动态导入(
importlib
)加载指定目录下的用例脚本,自动校验用例合法性(如是否继承指定基类、核心方法是否实现); - 核心逻辑:接收配置→解析用例路径→动态导入脚本→校验用例格式→统计可用用例数;
- 通用价值:无需硬编码导入用例,新增用例只需放在指定目录并配置路径,即可实现用例加载
(2)管控用例执行生命周期
- 功能说明 :标准化每个用例的执行流程------先执行前置步骤(
setUp
,如初始化请求客户端、登录),再按顺序执行测试步骤(如调用接口、数据校验),最后执行后置步骤(tearDown
,如清理测试数据、释放资源);过程中自动捕获异常(断言失败标记"失败",代码错误标记"错误"); - 核心逻辑:前置步骤→步骤执行→异常捕获→后置步骤→结果记录;
- 通用价值:确保所有用例执行流程统一,避免因个人编写习惯不同导致用例结构混乱,同时精准记录每个步骤的状态、耗时,便于问题定位。
(3)生成结构化执行结果
- 功能说明 :执行完成后,自动统计"测试套整体状态(通过/失败)、用例级详情(每个用例的状态、耗时)、步骤级详情(每个步骤的状态、错误日志)",并提供标准化方法(如
get_test_instances_details
)供Flask读取; - 核心逻辑:结果统计→结构化封装(字典格式)→提供读取接口;
- 通用价值:为Flask提供统一格式的结果数据,无需Flask二次处理,可直接封装为接口响应返回给前端,提升协同效率。
2. 核心价值总结
TestRunner的核心作用是"让自动化测试执行标准化、可追溯"------通过动态加载支持多业务用例,通过生命周期管控确保流程统一,通过结构化结果提供数据支撑,是系统的"自动化执行核心"。
四、测试用例编写:通用化组件组合与实践
系统通过"基础基类+通用业务组件"的设计,支持快速编写任意业务场景的测试用例(如电商订单、金融转账、教育选课)。核心思路是"复用通用组件,仅补充业务专属逻辑",以下是通用化组件说明、用例编写流程及示例。
1. 通用化核心组件说明
通用组件 | 作用说明 | 通用化设计(不绑定具体业务) |
---|---|---|
Base 类 |
所有用例的顶层基础类,封装"日志记录、步骤存储、前置/后置步骤模板" | 仅提供通用能力(如def_step 定义步骤、logger 输出日志),不包含任何业务逻辑,所有用例均可继承 |
BusinessBase 类 |
业务专属基类(继承Base ),负责"业务通用初始化" |
可根据业务场景扩展(如电商业务初始化"订单服务实例、商品数据库连接",金融业务初始化"转账服务、账户数据库") |
ApiBase 类 |
底层接口封装类,负责"构建请求URL、发起HTTP请求、处理通用请求配置(如Cookie、超时)" | 仅封装通用接口能力,不包含业务逻辑(如post /get 方法、_build_url 构建地址),所有业务的接口均可基于此类封装 |
BusinessService 类 |
业务服务组合类(依赖ApiBase ),负责"将零散接口组合成业务流程+加入业务断言" |
通用化业务逻辑封装(如"创建订单"="查询商品库存接口+创建订单接口+校验订单状态接口",并加入"库存足够""订单创建成功"的断言) |
DataFactory 类 |
测试数据工厂,负责"生成业务所需的测试数据" | 提供通用数据生成方法(如生成随机手机号、日期),同时支持业务专属数据扩展(如电商生成"订单数据",金融生成"转账金额数据") |
枚举类(TestStatus /StepStatus ) |
定义用例/步骤的标准状态(如"通过""待执行""失败") | 状态值通用(如PASS="通过" 、PENDING="待执行" ),适用于所有业务场景的状态校验 |
2. 通用化用例编写流程(五步完成)
无论测试何种业务(电商、金融、教育),均可按以下流程编写用例,仅需替换"业务专属组件"即可:
步骤1:选择/扩展BusinessBase
(业务基类)
根据测试业务,选择已有的BusinessBase
子类(如电商业务用EcommerceBase
,金融业务用FinanceBase
),或扩展新的业务基类(继承Base
)。核心是"复用业务通用初始化逻辑",避免重复代码。
通用示例(电商业务基类):
python
class EcommerceBase(Base):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# 业务通用初始化:初始化商品服务、订单服务、用户Cookie
self.goods_service = GoodsService() # 商品业务服务(依赖ApiBase)
self.order_service = OrderService() # 订单业务服务(依赖ApiBase)
self.user_cookie = kwargs.get("user_cookie") # 从配置获取用户Cookie
self.user_id = kwargs.get("user_id") # 从配置获取测试用户ID
# 初始化数据工厂(电商专属)
self.data_factory = EcommerceDataFactory(self.goods_service, self.order_service)
步骤2:封装ApiBase
与BusinessService
(业务服务)
-
第一步:基于
ApiBase
封装业务接口(如电商的"查询商品库存""创建订单"接口):pythonclass EcommerceApi(ApiBase): def __init__(self, base_url, cookie): super().__init__(base_url, cookie) # 复用ApiBase的请求客户端、URL构建能力 # 封装"查询商品库存"接口(仅发起请求,无业务逻辑) def get_goods_stock(self, goods_id): url = self._build_url(f"/api/goods/stock/{goods_id}") return self.client.get(url) # 封装"创建订单"接口(仅发起请求,无业务逻辑) def create_order(self, order_data): url = self._build_url("/api/order/create") return self.client.post(url, json=order_data)
-
第二步:基于
BusinessService
组合接口+断言(形成业务流程):pythonclass OrderService(BusinessService): def __init__(self, api: EcommerceApi): self.api = api # 依赖EcommerceApi self.logger = logging.getLogger(self.__class__.__name__) # 业务流程:创建订单(查询库存→创建订单→校验订单状态) def create_order_with_check(self, goods_id, user_id, buy_num): # 1. 第一步:查询商品库存(调用Api接口) stock_res = self.api.get_goods_stock(goods_id) if stock_res.status_code != 200: raise AssertionError(f"查询商品{goods_id}库存失败,状态码:{stock_res.status_code}") stock_num = stock_res.json()["stock"] # 2. 业务断言:库存足够 if stock_num < buy_num: raise AssertionError(f"商品{goods_id}库存不足(当前{stock_num},需{buy_num})") # 3. 第二步:创建订单(调用Api接口) order_data = {"goods_id": goods_id, "user_id": user_id, "buy_num": buy_num} create_res = self.api.create_order(order_data) if create_res.status_code != 200: raise AssertionError(f"创建订单失败,响应:{create_res.text}") order_id = create_res.json()["order_id"] self.logger.info(f"创建订单成功,订单ID:{order_id}") # 4. 第三步:返回订单ID(供后续步骤使用) return order_id
步骤3:扩展DataFactory
(生成业务数据)
根据业务需求,在DataFactory
中添加专属数据生成方法,同一个接口可能有很多的正交数据类型,避免用例内硬编码数据:
通用示例(电商数据工厂):
python
class EcommerceDataFactory(DataFactory):
def __init__(self, goods_service, order_service):
self.goods_service = goods_service
self.order_service = order_service
# 生成"合法订单数据"(依赖商品服务获取有效商品ID)
def create_valid_order_data(self, user_id):
# 业务逻辑:获取一个库存>0的商品ID
valid_goods_id = self.goods_service.get_valid_goods_id()
return {
"goods_id": valid_goods_id,
"user_id": user_id,
"buy_num": self.get_random_num(1, 5), # 通用方法:生成1-5的随机购买数量
"pay_type": self.get_random_enum(["WECHAT", "ALIPAY"]) # 通用方法:随机选择支付方式
}
# 生成"库存不足的订单数据"(用于测试异常场景)
def create_insufficient_stock_order_data(self, user_id):
invalid_goods_id = self.goods_service.get_insufficient_stock_goods_id()
return {
"goods_id": invalid_goods_id,
"user_id": user_id,
"buy_num": 10, # 购买数量大于库存
"pay_type": "WECHAT"
}
步骤4:编写用例类(继承BusinessBase
,补充业务步骤)
用例类仅需"继承业务基类,实现defineSteps
方法(定义测试步骤)",核心是"调用BusinessService
的业务方法,关联DataFactory
的测试数据":
通用示例(电商订单创建用例):
python
# 1. 导入通用组件与电商业务组件
from TestRunner.core.Base import EcommerceBase, StepStatus
from services.EcommerceService import OrderService
from utils.DataFactory import EcommerceDataFactory
from utils.Enum import PayType, OrderStatus
# 2. 定义用例名称(必须,TestRunner识别用例标识)
case_name = "电商_订单创建_正常流程测试"
# 3. 编写用例类(继承电商业务基类EcommerceBase)
class test_ecommerce_order_create_normal(EcommerceBase):
# 前置步骤:复用基类初始化,补充用例专属配置(可选)
def setUp(self):
super().setUp() # 执行基类前置逻辑(初始化服务、数据工厂)
self.target_goods_id = self.data_factory.get_valid_goods_id() # 业务专属:获取有效商品ID
# 核心:定义测试步骤(必须实现defineSteps方法)
def defineSteps(self):
# 步骤1:清理用户历史未支付订单(环境清理,避免影响当前测试)
self.def_step(
step_name="【环境清理】删除用户未支付订单",
method=self.order_service.delete_unpaid_orders, # 调用业务服务方法
kwargs={"user_id": self.user_id} # 业务参数:从基类继承的用户ID
)
# 步骤2:生成合法订单数据(调用数据工厂)
self.def_step(
step_name="【数据准备】生成合法订单数据",
method=self.data_factory.create_valid_order_data, # 调用数据工厂方法
kwargs={"user_id": self.user_id},
# 注意:此处可将数据工厂结果赋值给变量,供后续步骤使用
# (TestRunner支持步骤间参数传递,需在基类中定义临时变量)
)
# 步骤3:创建订单并校验(调用业务服务,包含接口组合+断言)
self.def_step(
step_name="【业务执行】创建订单并校验库存、订单状态",
method=self.order_service.create_order_with_check, # 调用业务服务的核心方法
kwargs={
"goods_id": self.target_goods_id,
"user_id": self.user_id,
"buy_num": 2 # 购买数量(可从数据工厂获取,此处简化示例)
}
)
# 步骤4:校验订单状态为"待支付"(调用业务服务的校验方法)
self.def_step(
step_name="【状态校验】订单状态为待支付",
method=self.order_service.check_order_status, # 业务校验方法
kwargs={
"order_id": self.order_service.last_order_id, # 步骤3返回的订单ID(业务服务存储)
"expected_status": OrderStatus.UNPAID.value # 通用枚举:待支付状态
}
)
# 后置步骤:清理测试数据(可选)
def tearDown(self):
super().tearDown() # 执行基类后置逻辑(基础日志)
# 业务专属清理:删除当前用例创建的订单
self.order_service.delete_order(self.order_service.last_order_id)
self.logger.info(f"===== 用例{case_name}执行完成,已清理测试数据 =====")
步骤5:配置用例路径,接入系统执行
将编写好的用例脚本放在指定目录(如test_cases/ecommerce/
),在Flask的配置中添加"业务类型-用例路径"映射通过Flask接口触发执行。
3. 新业务用例编写总结(通用化思路)
无论新增何种业务的测试用例,均可遵循以下通用步骤,实现快速接入:
- 搭业务基类 :继承
Base
类,实现"业务通用初始化"(如服务实例、数据库连接、用户配置); - 封接口与服务 :基于
ApiBase
封装业务接口,基于BusinessService
组合接口成业务流程+断言; - 造测试数据 :扩展
DataFactory
,生成业务所需的"正常数据""异常数据"; - 写用例步骤 :继承业务基类,在
defineSteps
中调用"业务服务方法+数据工厂",定义测试步骤; - 配路径执行:将用例放在指定目录,配置"业务类型-用例路径"映射,通过Flask接口触发。
五、系统整体价值与扩展建议
1. 整体价值
- 通用化适配:不绑定具体业务,可快速接入电商、金融、教育等各类业务的自动化测试需求;
- 低代码编写:复用通用组件,编写新用例仅需补充业务专属逻辑,降低测试人员技术门槛;
- 全流程可控:从"发起请求→执行测试→查询结果"全流程可监控、可追溯,便于团队协作;
- 工具链集成:通过Flask标准化接口,可无缝接入CI/CD流水线、测试平台,推动自动化测试融入研发流程。
2. 扩展建议
- 接口层扩展:新增"任务取消""结果导出(Excel/PDF)"接口,提升用户体验;
- 执行层扩展:TestRunner支持"用例优先级""并发控制",适配大规模用例执行场景;
- 监控层扩展:新增"执行告警"功能(如用例失败时发送邮件/钉钉通知),提升问题响应速度。