开源三合一自动化测试平台:Docker一键部署,统一Web、API、App测试
1. 项目概述:一个全栈测试工程师的“瑞士军刀”
干了十多年测试,从手工点点点到脚本满天飞,再到搞平台、带团队,我最大的感受就是:测试工具链太碎了。UI自动化用一套框架,接口测试用另一套工具,移动端App测试又是完全不同的生态。团队里新人上手成本高,老手维护多套环境也头疼,更别提测试报告分散、数据难以统一分析了。所以,我一直想搞一个“大一统”的玩意儿,能把UI、接口、App这三块最核心的自动化测试给揉到一起,让部署和上手变得跟搭积木一样简单。
这就是我开源这个自动化测试平台的初衷。你可以把它理解为一个测试领域的“瑞士军刀”,核心目标就一个:用一套平台,解决Web UI、API接口、移动端App(Android/iOS)三大主流自动化测试需求,并且通过Docker实现真正意义上的“一键部署”。它不是为了替代那些顶级的、功能极其专精的商业工具,而是为中小团队、个人开发者、或者想快速搭建内部测试能力的企业,提供一个开箱即用、五脏俱全、且能自主掌控的解决方案。如果你正被Selenium、Postman、Appium、Jenkins等各种工具和它们的集成问题搞得焦头烂额,那这个平台可能就是你要找的“降压药”。
2. 平台核心架构与设计思路拆解
2.1 为什么是“三合一”架构?
市面上优秀的单一领域测试工具很多,比如做UI自动化的Selenium/Playwright,做接口测试的Postman/Apifox,做移动端测试的Appium。但问题在于,一个完整的业务测试流程往往是跨端的:一个电商下单流程,可能涉及H5页面(UI)、调用支付接口(API)、在App内查看订单(移动端)。如果三个环节用三套独立的工具,脚本语言可能不同(Python/Java/JavaScript),报告格式各异,调度依赖需要额外集成(比如用Jenkins串起来),维护成本和复杂度呈指数级上升。
我的设计思路是“平台化整合,标准化输出”。平台本身不重复造轮子,而是作为调度中枢和标准制定者,去集成和封装这些领域内最成熟的开源引擎。
- UI测试层:基于Selenium/Playwright封装。为什么选它们?生态成熟、社区活跃、浏览器支持好。平台提供一套更上层的、面向业务的Page Object模式封装,让测试脚本编写者不用太关心底层Driver的初始化、等待策略等繁琐细节,更专注于业务逻辑和元素定位。同时,平台统一管理浏览器驱动,解决版本兼容性这个经典痛点。
- 接口测试层:核心引擎是HTTP客户端库(如Requests),但关键在于提供了比Postman更利于持续集成(CI)的协作模式。平台将接口用例、环境变量、前置/后置脚本(如数据库操作、加解密)、断言规则全部结构化存储,支持从Swagger/OpenAPI直接导入,并生成清晰的数据驱动测试用例。它解决了Postman用例难以版本化、团队共享协作不便的问题。
- App测试层:坚定地站在Appium的肩膀上。Appium的“一次编写,多端运行”(Android/iOS)理念与我们的平台目标高度一致。平台主要解决的是Appium环境搭建复杂的问题,通过Docker镜像预置所有依赖(Android SDK、模拟器、Appium Server等),并提供统一的设备管理、应用安装卸载、Capabilities配置界面。
平台的核心价值,就在于用一个统一的Web界面来管理这三类测试资产(用例、脚本、数据),用一个统一的调度引擎来触发和执行它们,最后用一个统一的报告系统来展示结果。技术栈上,后端选用Python/Go这类生态好的语言,前端用Vue/React,数据库用MySQL/PostgreSQL,消息队列用Redis/RabbitMQ处理异步任务,所有组件最终通过Docker Compose编排。
2.2 Docker化部署:从“地狱”到“一键”的关键抉择
“支持Docker一键部署”不是一句轻飘飘的口号,而是这个平台能否真正被用起来的关键。传统测试环境搭建,尤其是带UI和移动端模拟器的,堪称“环境配置地狱”。不同操作系统、不同浏览器版本、不同JDK/Node.js/Python版本、不同Android SDK Build-Tools……任何一个环节出问题,都足以让人崩溃半天。
Docker化彻底解决了这个问题。我的做法是提供多个精心构建的Docker镜像:
- 核心服务镜像:包含平台后端、前端、数据库、消息队列等基础服务。
- 测试执行节点镜像:这是一个关键设计。这个镜像里预装了完整的测试执行环境:Python/Java运行环境、Chrome/Firefox浏览器、对应的WebDriver、Android模拟器(或连接真机的所需工具)、Appium Server等。平台可以动态扩展多个这样的执行节点。
用户要做的,仅仅是在宿主机上安装好Docker和Docker Compose,然后执行我们提供的一个docker-compose up -d命令。剩下的所有依赖下载、服务启动、网络配置,全部由Docker Compose文件定义好。这对于想快速体验的开发者、需要统一测试环境的团队、或者希望在CI/CD流水线中动态创建测试环境的场景,是巨大的效率提升。
注意:由于Docker容器内运行UI测试(特别是需要显示渲染的)和Android模拟器涉及图形界面和硬件加速,需要在
docker run命令或Compose文件中添加特定的参数,例如-e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix(用于X11转发)或使用--privileged权限并安装xvfb(虚拟帧缓冲区)来模拟显示。这部分在平台的部署文档中会详细说明,是初学者最容易踩坑的地方。
3. 核心功能模块深度解析
3.1 项目管理与测试资产组织
平台以“项目”为最高维度进行隔离,适合多个团队或产品线并行使用。在一个项目内,测试资产被清晰地分层管理:
- 测试用例:分为UI用例、接口用例、App用例。每个用例包含名称、描述、标签、所属模块等元信息。
- 测试步骤:对于UI和App测试,步骤对应一系列原子操作(点击、输入、断言等);对于接口测试,步骤对应单个API请求。
- 测试数据:支持全局变量、环境变量(如测试/预发/生产环境的不同域名)和数据驱动。数据驱动是亮点,你可以准备一个CSV或JSON文件,定义多组输入数据和预期结果,一个用例模板就能自动运行多次,极大提高覆盖率。
- 页面对象/接口对象:这是为了提升脚本可维护性。UI测试中,将每个网页抽象成一个“页面对象”,其中封装了该页面的所有元素定位器和常用操作方法。接口测试中,可以将一个微服务的所有接口定义为一个“接口集合”,统一管理Host、Header、Auth等信息。变更时只需修改一处。
3.2 可视化用例编排与脚本编辑
平台提供了两种用例创作模式,兼顾了不同角色的需求:
- 低代码可视化编排:通过拖拽方式,将“打开浏览器”、“输入文本”、“点击元素”、“验证响应”等积木块组合成测试流程。这非常适合测试人员快速创建冒烟测试或简单流程测试,无需编码。平台后台会将拖拽操作转化为对应的Python/JavaScript脚本。
- 专业代码编辑器:为开发者和资深测试提供完整的IDE体验,支持语法高亮、代码补全、调试。你可以直接编写基于Page Object的UI脚本、灵活的接口请求脚本或Appium原生脚本。平台提供丰富的内置函数库,比如数据库查询、Redis操作、文件读写、加解密工具等,让你在脚本中能轻松处理各种测试前置和后置条件。
一个接口测试的代码示例:
import requests from platform_sdk import assert_utils, db_utils # 从环境变量获取基础URL base_url = env.get("API_BASE_URL") # 从数据驱动中获取本轮测试的username username = data.get("username") # 1. 前置操作:清理测试数据 db_utils.execute("DELETE FROM users WHERE username = %s", (username,)) # 2. 执行接口请求 payload = {"username": username, "password": "Test@123"} headers = {"Content-Type": "application/json"} response = requests.post(f"{base_url}/auth/login", json=payload, headers=headers) # 3. 断言 assert_utils.equal(response.status_code, 200) assert_utils.json_path_exists(response.json(), "$.token") token = response.json()["token"] # 4. 后置操作:将token存入上下文,供后续用例使用 context.set("auth_token", token)这个例子展示了如何在一次接口测试中,串联起数据库操作、API调用、复杂断言和上下文传递,这些都是实际项目中非常常见的需求。
3.3 强大的调度与执行引擎
测试用例写好了,怎么跑?平台提供了灵活的触发方式:
- 手动触发:在Web界面上点选用例或套件立即执行。
- 定时任务:像Cron Job一样,设置每天凌晨执行回归测试套件。
- CI/CD集成:平台暴露了标准的RESTful API。你可以在Jenkins、GitLab CI、GitHub Actions的Pipeline中,简单地调用一个API接口,触发对应的测试任务,并获取测试结果。这是实现“持续测试”的关键。
- 测试套件与依赖:可以将多个用例组合成测试套件,并设置用例间的执行依赖关系。例如,“登录”用例必须在“购物车结算”用例之前成功执行。
执行引擎是异步的。当你触发一个任务后,任务会被放入消息队列,由空闲的“测试执行节点”(Docker容器)消费并执行。这意味着:
- 支持高并发:可以启动多个执行节点同时跑不同的测试任务,大幅缩短整体测试时间。
- 资源隔离:每个任务在独立的容器环境中运行,互不干扰,避免了环境污染。
- 稳定性提升:单个测试任务的失败(甚至容器崩溃)不会影响平台主服务和其他任务的执行。
3.4 全景式测试报告与数据分析
测试报告再也不是简单的“通过/失败”了。平台会收集每一次执行的完整数据,生成多维度的报告:
- 概览仪表盘:展示项目整体的通过率、失败率、执行趋势图、耗时最长的用例等。
- 详尽的执行详情:
- UI测试:自动录制操作过程,生成GIF或视频(基于FFmpeg),并附带每一步的截图。断言失败时,高亮显示失败时的页面状态。
- 接口测试:展示完整的请求和响应信息(Header、Body)、断言详情、以及关联的日志。
- App测试:同样提供操作录屏和关键步骤截图。
- 日志聚合:将测试脚本中的
print日志、框架日志、服务端日志统一收集、分级(INFO/DEBUG/ERROR)展示,方便排查问题。 - 历史对比:可以对比同一用例不同时间点的执行结果,快速定位是代码变更还是环境问题导致的失败。
这些数据不仅用于查看单次结果,更重要的是能沉淀为测试资产。平台提供简单的数据分析功能,比如“过去一个月哪个模块的失败率最高?”、“哪些接口的响应时间在持续变慢?”,为产品质量和性能优化提供数据支撑。
4. 从零到一的完整部署与配置实操
4.1 基础环境准备与Docker部署
假设你在一台干净的Linux服务器(Ubuntu 20.04+)上部署。
第一步:安装Docker与Docker Compose
# 安装Docker curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh sudo usermod -aG docker $USER # 将当前用户加入docker组,避免每次sudo newgrp docker # 刷新用户组,或重新登录 # 安装Docker Compose (v2) sudo apt-get update sudo apt-get install docker-compose-plugin # 验证安装 docker compose version第二步:获取平台部署文件平台的所有部署配置都放在一个Git仓库里。
git clone https://github.com/your-repo/auto-test-platform-deploy.git cd auto-test-platform-deploy这个仓库里最关键的文件是docker-compose.yml和.env环境配置文件。
第三步:配置环境变量编辑.env文件,这是配置的核心。你需要关注几个关键项:
# 数据库配置 MYSQL_ROOT_PASSWORD=your_strong_password MYSQL_DATABASE=test_platform MYSQL_USER=platform_user MYSQL_PASSWORD=user_password # Redis配置(用于缓存和消息队列) REDIS_PASSWORD=redis_password # 平台访问地址(用于报告中链接的生成) PLATFORM_URL=http://your-server-ip:8080 # 测试节点相关(如果需要执行UI/App测试) # 设置X11转发相关,如果服务器无图形界面,则使用XVFB ENABLE_XVFB=true # 设置VNC端口,方便远程查看执行情况(可选) VNC_PORT=5901 VNC_PASSWORD=vncpassword第四步:启动平台
# 一键启动所有服务(核心平台+一个测试执行节点) docker compose up -d启动后,用docker compose ps查看所有容器状态,确保都是“Up”状态。首次启动会拉取镜像,可能需要几分钟。
第五步:访问与初始化打开浏览器,访问http://your-server-ip:8080。首次访问会进入初始化页面,引导你创建管理员账号,并配置数据库连接(通常就是.env里配置的,会自动填充)。完成后,你就进入了平台的主界面。
实操心得:部署看似简单,但90%的问题出在
.env配置和宿主机权限上。特别是涉及UI测试时,如果ENABLE_XVFB=true仍无法启动浏览器,需要检查Docker容器是否以--privileged模式运行(在Compose文件中已配置),以及/tmp/.X11-unix的挂载是否正确。建议先在本地开发环境(Mac/Windows Docker Desktop)上部署测试,再上服务器。
4.2 第一个测试项目:Web UI登录测试实战
让我们创建一个实际的UI测试用例,目标是测试一个登录页面。
1. 创建项目与模块在平台中,点击“新建项目”,命名为“电商平台测试”。在项目内,创建模块“用户认证”。
2. 定义页面对象在“页面对象”仓库,新建一个名为LoginPage的页面对象。
- URL:
/login - 元素定位:
- 用户名输入框:
id=username - 密码输入框:
css=input[type='password'] - 登录按钮:
xpath=//button[text()='登录'] - 错误提示:
class=alert-error
- 用户名输入框:
- 操作方法(平台可自动生成骨架):
def input_username(self, username): self.driver.find_element(By.ID, "username").send_keys(username) def input_password(self, password): self.driver.find_element(By.CSS_SELECTOR, "input[type='password']").send_keys(password) def click_login_button(self): self.driver.find_element(By.XPATH, "//button[text()='登录']").click() def get_error_message(self): return self.driver.find_element(By.CLASS_NAME, "alert-error").text
3. 编写测试用例新建一个UI测试用例,命名为“验证登录失败场景”。
- 步骤1(使用可视化编排):拖入“打开浏览器”块,选择Chrome,URL填入
{{BASE_URL}}/login(BASE_URL是环境变量)。 - 步骤2:拖入“输入文本”块,选择页面对象
LoginPage的input_username方法,参数填test_user。 - 步骤3:拖入“输入文本”块,选择
input_password,参数填wrong_password。 - 步骤4:拖入“点击元素”块,选择
click_login_button。 - 步骤5:拖入“断言”块,选择
get_error_message的返回值,“包含” “用户名或密码错误”。
4. 配置环境与执行在“环境管理”中,添加一个“测试环境”,变量BASE_URL设置为http://test.your-app.com。 回到用例,选择“测试环境”,点击“执行”。平台会自动分配一个执行节点,启动浏览器,完成上述操作,并生成报告。
5. 进阶:数据驱动如果我想测试多个错误的用户名密码组合呢?在用例编辑界面,切换到“数据驱动”标签,上传一个CSV文件:
username,password,expected_error test_user,wrong_pass,用户名或密码错误 empty_user,,请输入用户名 test_user,short,密码长度不足然后在断言步骤中,将硬编码的预期错误信息改为变量{{expected_error}}。平台会自动为CSV中的每一行数据运行一次用例。
4.3 集成到CI/CD流水线(以GitLab CI为例)
自动化测试只有集成到开发流程中才能发挥最大价值。以下是一个.gitlab-ci.yml的示例片段:
stages: - build - test - deploy ui_and_api_test: stage: test image: docker:latest services: - docker:dind variables: # 假设测试平台部署在 http://test-platform.internal PLATFORM_URL: "http://test-platform.internal" PROJECT_ID: "123" SUITE_ID: "456" API_TOKEN: "$TEST_PLATFORM_TOKEN" # Token在GitLab CI变量中设置 script: - | # 1. 调用平台API,触发测试套件执行 RESPONSE=$(curl -X POST "${PLATFORM_URL}/api/v1/task/trigger" \ -H "Authorization: Bearer ${API_TOKEN}" \ -H "Content-Type: application/json" \ -d "{\"project_id\": ${PROJECT_ID}, \"suite_id\": ${SUITE_ID}, \"env\": \"ci\"}") TASK_ID=$(echo $RESPONSE | jq -r '.data.task_id') echo "Triggered task: $TASK_ID" # 2. 轮询任务状态,直到完成(超时设置300秒) for i in {1..30}; do STATUS=$(curl -s "${PLATFORM_URL}/api/v1/task/${TASK_ID}/status" \ -H "Authorization: Bearer ${API_TOKEN}" | jq -r '.data.status') echo "Task status: $STATUS" if [ "$STATUS" = "FINISHED" ]; then break elif [ "$STATUS" = "FAILED" ] || [ "$STATUS" = "ERROR" ]; then echo "Task execution failed." exit 1 fi sleep 10 done # 3. 获取最终报告,判断是否通过 REPORT=$(curl -s "${PLATFORM_URL}/api/v1/task/${TASK_ID}/report/summary" \ -H "Authorization: Bearer ${API_TOKEN}") PASS_RATE=$(echo $REPORT | jq -r '.data.pass_rate') echo "Test pass rate: ${PASS_RATE}%" # 4. 如果通过率低于阈值(如95%),则标记CI任务失败 if (( $(echo "$PASS_RATE < 95" | bc -l) )); then echo "Test pass rate is below threshold. Failing the pipeline." exit 1 fi only: - merge_requests # 仅在合并请求时触发 - main # 或在主干分支推送时触发这个配置实现了在每次合并请求或主干提交时,自动触发平台的测试套件,并根据测试结果决定CI流水线是否通过。API_TOKEN需要在平台的用户设置中生成,并妥善保存在GitLab的CI/CD变量里。
5. 避坑指南与效能提升技巧
在实际使用和推广这个平台的过程中,我积累了不少血泪教训,这里分享几个最关键的点。
5.1 环境与执行稳定性保障
问题1:UI测试在Docker中运行不稳定,经常超时或无法启动浏览器。
- 排查:这通常是资源不足或显示问题。首先,确保执行节点的Docker容器分配了足够的内存(建议至少2GB)和CPU。其次,对于无头浏览器测试,务必使用
xvfb-run命令来启动浏览器,或者使用Chrome/Firefox的--headless模式。我们的执行节点镜像已经内置了XVFB。 - 技巧:在测试脚本中,一定要使用显式等待(WebDriverWait),而不是硬性的
time.sleep()。网络波动或页面加载慢会导致元素找不到,显式等待更健壮。from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By # 不好的做法 time.sleep(5) element = driver.find_element(By.ID, "dynamic-element") # 好的做法 wait = WebDriverWait(driver, 10) # 最多等10秒 element = wait.until(EC.presence_of_element_located((By.ID, "dynamic-element")))
问题2:并行执行测试时,用例间相互干扰。
- 排查:这是测试隔离没做好。确保每个测试用例在执行前,都处于一个干净的状态。例如,通过API或数据库操作清理前一个测试创建的数据。
- 技巧:利用平台提供的
setUp和tearDown钩子函数(或注解)。在setUp里初始化独立的数据(如创建一个唯一的用户名),在tearDown里清理。平台的设计是每个用例在一个独立的临时容器中执行,文件系统和进程是隔离的,但数据库和外部服务是共享的,所以数据清理至关重要。
5.2 测试脚本维护性优化
问题:页面元素一变动,大量测试脚本报错,维护成本高。
- 解决方案:严格执行Page Object Model (POM)设计模式。就像前面的
LoginPage例子,所有元素定位器和页面操作方法都集中定义在页面对象类中。当页面元素ID或CSS选择器变更时,你只需要修改这一个页面对象文件,所有引用该元素的测试用例都会自动生效。 - 进阶技巧:在页面对象中,不要写死定位器字符串,而是使用定位器字典或配置文件。这样,甚至可以将元素定位信息交给非技术人员维护。
# locators.py LOGIN_PAGE_LOCATORS = { "username_input": ("id", "username"), "password_input": ("css", "input[type='password']"), "login_button": ("xpath", "//button[text()='登录']") } # login_page.py from locators import LOGIN_PAGE_LOCATORS class LoginPage: def input_username(self, username): locator_type, locator_value = LOGIN_PAGE_LOCATORS["username_input"] self.driver.find_element(getattr(By, locator_type.upper()), locator_value).send_keys(username)
5.3 平台性能与团队协作
问题:随着用例增多,执行速度变慢,报告查看卡顿。
- 数据库优化:测试执行记录、步骤详情、日志这些数据增长非常快。要定期(如每月)归档历史数据到备份表,并对核心查询表(如
test_case,task)的常用字段(project_id,status,create_time)建立索引。 - 执行策略优化:
- 分片执行:将大的测试套件拆分成多个小套件,并行执行。
- 增量测试:与版本控制系统(Git)集成,只运行与本次代码变更相关的测试用例。这需要平台能解析代码变更影响的范围,实现起来较复杂,但收益巨大。一个简单的起点是给用例打上标签(如
@module_user),然后根据提交信息中的关键词决定运行哪些标签的用例。
- 团队协作:平台支持基于角色的权限控制(RBAC)。可以为不同成员分配“浏览者”、“测试员”、“开发员”、“管理员”等角色,控制其对项目、用例、执行任务的查看和操作权限。建立良好的用例评审和版本管理流程,利用平台的“分支”或“版本”功能来管理测试资产的不同迭代。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| Docker Compose启动失败,端口冲突 | 8080、3306、6379等端口被占用 | 修改.env或docker-compose.yml中的端口映射,如8080:8080改为8081:8080 |
| 平台Web界面能打开,但登录后无法加载项目 | 前端静态资源加载失败或后端API服务未启动 | 检查浏览器控制台(F12)网络请求错误;docker compose logs backend查看后端日志 |
| UI测试执行失败,提示“无法连接到Chrome” | Chrome浏览器或Chromedriver版本不匹配;Docker容器内显示问题 | 确保执行节点镜像中的Chrome与Chromedriver版本兼容;确认已启用XVFB或无头模式 |
| 接口测试返回超时 | 被测服务地址错误或网络不通;平台执行节点无法访问该地址 | 检查环境变量中的BASE_URL;从执行节点容器内部curl一下被测服务地址 |
| App测试无法检测到手机/模拟器 | Appium服务未启动;设备UDID未正确配置;ADB连接问题 | 查看执行节点容器日志中Appium的启动状态;确认设备管理页面中设备状态为“在线”;检查USB连接或模拟器端口映射 |
| 测试报告中没有截图或录屏 | 截图功能未开启;存储路径权限不足;FFmpeg未安装 | 在平台设置中启用截图/录屏功能;检查Docker卷挂载的目录权限;在执行节点镜像中安装FFmpeg |
这个平台从构思到开源,经历了无数次的迭代和踩坑。它可能不是功能最强大的,但我坚信它在“开箱即用”、“降低门槛”、“统一体验”这几个点上做到了很好的平衡。对于想要快速搭建内部自动化测试体系,又不想被繁杂的运维和集成困扰的团队来说,它提供了一个可靠的起点。开源出来,是希望它能帮助到更多有同样痛点的同行,也期待社区的反馈能让它变得更好。
