什么是自动化测试框架?
框架并非仅仅是引入一些依赖库、合并几个类。它是一个综合的解决方案,涉及设计模式、模块化、可重用性等方面。
自动化测试框架的目的是提供一套标准和规则、从而使自动化测试的开发和维护更高效、更易于扩展
构建合适的框架至关重要,使用结构良好的框架可以提高团队效率,提高测试准确性,最大限度地提高测试覆盖率,降低成本和维护费用——最终带来更高的投资回报率 (ROI)。
如何从零开始构建自动化测试框架
简单的说,构建自动化测试框架需要将框架视作一个应用或产品一样去对待,构建自动化测试框架分为以下步骤:
- 需求分析和调研
- 框架选型和决策
- 框架设计
Step1 - 需求分析和调研
在需求分析和调研阶段,除了明确测试对象和框架的使用场景,还需要深入了解和评估框架的潜在用户(如开发人员、测试人员以及CI/CD工具)的需求。以下是一些关键问题,可以帮助更全面地定义框架的需求:
-
框架的适用性和覆盖范围
- 框架是否需要支持跨平台测试(如Web、移动端、桌面端)?
- 框架主要覆盖哪些测试用例和测试场景(功能测试、性能测试、回归测试等)?
- 哪些用例是业务价值较高、需要优先自动化的?
-
技术依赖与交互
- 测试时是否需要与数据库或外部接口交互?这些交互是否需要模拟环境或使用真实数据?
- 框架是否需要支持特定的技术栈或与现有工具(如Jenkins、GitLab CI等)集成?
-
可扩展性与灵活性
- 框架未来可能面临哪些变化和扩展需求(如支持新的测试场景、新技术或更高的并发量)?
- 需要支持的测试用例数量是多少?框架是否需要设计为可扩展以满足未来增长需求?
-
使用者分析
- 框架的主要使用和维护人员是谁?他们的技术背景和能力如何(如开发人员是否擅长编程,测试人员是否熟悉框架工具)?
- 是否需要提供友好的界面或详细的文档以降低框架的使用门槛?
-
测试驱动类型
在不同项目中,测试框架可能需要支持不同的测试驱动方式,选择合适的驱动方式可以提高测试的可维护性、可读性和可扩展性。以下是几种常见的测试驱动类型,后面将在选型决策部分详细介绍其适用场景和工具选择:
- 数据驱动测试(DDT - Data-Driven Testing)
- 行为驱动测试(BDD - Behavior-Driven Development)
- 关键字驱动测试(KDT - Keyword-Driven Testing)
- 混合驱动测试(Hybrid-Driven Testing)
- 无驱动类型 一般不推荐,测试用例规模很小或短期使用可以
Step2 - 选型决策
经过初步需求分析后,我们对测试框架或工具的构思和目标有了较为清晰的方向。接下来,需要围绕框架的功能和技术实现进行具体的决策,包括以下几个方面:
框架功能定义
测试框架需要包括哪些功能 ,比如
- 测试数据管理:支持多种测试数据的管理与生成方式,如静态数据、动态生成数据,或从数据库提取的数据。
- Mock 功能:通过模拟外部服务或接口的响应,提升测试效率,减少对真实环境的依赖。
- 日志记录:在测试失败时,生成详细日志,包含步骤、数据输入和错误堆栈等信息,便于排查问题。
- 测试环境可配置: 框架应能轻松切换测试环境(开发、测试、生产),通过配置文件实现灵活的环境管理。
- 报告生成:通过工具(如Allure、ExtentReports)自动生成结构化、直观的测试报告,包括统计分析、趋势图和失败详情。
- 记录和存储测试结果:这里可以讨论如何使用报告生成工具(如Allure, ExtentReports)生成美观的测试报告,如何存储历史测试结果供以后分析。
框架选型
根据框架的功能、需求、使用人员的技术背景等综合考量选择适合的编程语言、主要开发策略、测试驱动类型和依赖和集成的库或框架。
切记不要对某种技术或框架工具执迷不悟,尽管一些工具或框架可能更现代化、更先进,但如果与项目需求和实际背景不符,其价值将大打折扣。
以下是选型时的关键考量因素:
编程语言 优先选择团队技术背景内熟悉的语言,如Python、Java、JavaScript等。
开发策略 根据项目需求和资源状况决定开发策略:
- 使用成熟的开源框架:充分利用已有工具(如 Selenium、Cypress、Appium 等)的功能,快速实现核心测试需求。
- 适度自研或二次开发:在开源框架基础上定制化开发,填补特定场景的功能空白,提高框架的适配性和灵活性。
选择合适的测试驱动类型
测试框架的核心在于如何驱动测试,不同的测试驱动方式适用于不同的业务需求。以下是几种常见的测试驱动类型及其特点:
-
数据驱动测试(DDT - Data-Driven Testin)
适用于使用不同的数据集运行相同的测试用例,通常用于API 测试、回归测试、边界测试等。例如,验证一个登录功能时,可以使用不同的用户名/密码组合来执行相同的测试逻辑。
📌 典型场景:API 测试、表单输入验证、支付流程测试。
-
行为驱动测试(BDD - Behavior-Driven Development)
适用于测试、开发、产品经理协同编写测试用例,采用Gherkin 语法(Given-When-Then)来增强可读性。BDD 强调业务逻辑驱动,而非技术实现,使非技术人员也能理解测试用例。
📌 典型场景:敏捷开发团队、跨职能协作、需求自动化验证。
-
关键字驱动测试(KDT - Keyword-Driven Testing)
适用于低代码/无代码测试,测试用例由预定义的**关键字(Keywords)**组成,例如 “Login”, “Click Button” 等,使非技术人员可以编写自动化测试。
📌 典型场景:企业内部测试团队、测试人员不具备编程能力的场景、UI 自动化测试。
-
混合驱动测试(Hybrid-Driven Testing)
结合数据驱动测试(DDT)和关键字驱动测试(KDT),或者结合BDD等方法,实现灵活性和扩展性。例如,在 BDD 框架中嵌入数据驱动,让业务人员能用 Gherkin 语法编写测试场景,同时测试数据可以参数化。
📌 典型场景:大规模 UI 自动化测试、API 自动化测试、支持多数据集的业务流程测试。
基础测试框架和工具选择
基础测试框架
- Web 测试:如 Selenium(跨浏览器支持)、Cypress(现代 Web 应用)和 Playwright(高性能多浏览器支持)。
- 移动测试:如 Appium,用于跨平台的移动应用测试。
- API 测试:如 RestAssured、Postman 或 Karate,满足接口测试需求。
测试驱动库
- JUnit/TestNG(Java):单元测试框架,支持断言与数据驱动。
- Pytest(Python):灵活且强大,支持插件与参数化测试。
- Mocha/Jest(JavaScript):适合前端,支持异步测试。
报告生成库
- Allure:生成详细 HTML 报告,兼容多框架。
- ExtentReports:高级自定义报告,适用于 Java 和 C# 项目。
接着我们举个例子
背景
一家技术团队正在开发一个多平台的电子商务应用,包含Web端和移动端。团队的目标是通过自动化测试框架覆盖核心功能(如用户登录、商品搜索、下单支付等),并与现有的CI/CD流程集成。
团队情况
- 开发语言主要是 Python JavaScript。
- 团队中测试人员对自动化测试的经验不一,其中一部分成员熟悉 Selenium,对Cypress 和 Playwright接触较少
- 项目时间紧张,需要快速迭代和测试反馈。
需求分析
- 浏览器支持:需要覆盖 Chrome 和 Firefox,同时考虑移动端的 WebView 测试。
- 测试类型:主要是功能测试, 主流程的回归为主
- 集成需求:需要与 Jenkins 的 CI 流程无缝集成。
- 扩展性:测试用例未来可能从几十增长到上百或更多,框架需要具备可扩展可复用性, 易于维护。
综合考量与决策
团队在选择测试框架时,充分考虑了工具的优缺点、团队熟悉程度、项目的短期与长期需求,决定采用 混合驱动测试(Hybrid-Driven Testing),行为驱动测试BDD结合**数据驱动DDT,**以提升测试的可维护性和扩展性,并分两个阶段逐步落地测试框架。
第一阶段
- 快速搭建自动化测试框架,满足核心功能的回归测试需求。利用团队熟悉的Selenium 作为 Web 测试框架,并结合Pytest进行测试驱动执行。
- 实现数据驱动测试(DDT),使登录、搜索、支付等测试支持不同的数据输入,提高覆盖率。
第二阶段
- 逐步引入Playwright作为主力测试工具,取代Selenium提升测试执行速度和维护性。
- 逐步引入BDD(如 Cucumber)来增强业务可读性,并使测试更贴近产品需求。
附上Selenium Playwright优缺点分析
特性 | Selenium | Playwright |
---|---|---|
优点 | ||
成熟性与兼容性 | 支持多语言(Python、Java、JavaScript)和多种浏览器(Chrome、Firefox、Safari)。 | 支持多语言(JavaScript、Python、C#)和多浏览器(Chromium、Firefox、WebKit)。 |
社区与生态 | 拥有广泛的社区支持,丰富的文档、教程和插件。 | 社区和生态尚在发展中,但逐步增加支持和文档。 |
灵活性 | 能处理复杂场景,如多窗口操作、iframe 切换、文件上传等。 | 内置功能强大,支持截图、PDF 生成、地理位置模拟、网络拦截等功能。 |
动态内容支持 | 需要显式处理异步操作(手动等待)。 | 原生支持动态内容等待,无需显式处理异步操作。 |
性能与并行测试 | 性能较慢,分布式并行测试需依赖 Selenium Grid。 | 高性能,原生支持高效的并行测试。 |
缺点 | ||
设置和调试 | 依赖 WebDriver,配置和维护复杂。 | 简化了设置和调试流程,学习曲线尚可。 |
现代化支持 | 对单页应用(SPA)的支持较弱。 | 对现代 Web 技术支持优秀,适合动态页面。 |
学习成本 | 团队成员通常熟悉,学习成本低。 | 相对较新,需要时间适应 API 和用法。 |
Step3 框架具体设计
在构建测试自动化框架时,良好的设计模式和架构规划至关重要。如果一开始没有架构设计,就会导致循环依赖和紧密耦合的代码,会使框架难以理解和维护。
自动化测试框架中常用的架构风格是分层架构设计,每一层有特定的职责。一般自动化框架层将按如下方式分离:
-
展示层
展示层是分层架构中的顶级层。在测试自动化中,测试层将成为我们的展示层,它将充当用户接口。
-
业务层
将业务逻辑放在业务层,在WebUI的测试自动化中,我们可以在页面类中编写业务逻辑,因此页面对象(POM)可以归入该业务层。或者单独抽象出业务层以进一步解耦。
-
页面层
页面层是WebUI自动化测试框架中的关键部分,它主要负责和被测试的页面交互,包括页面元素定位、页面交互等。
-
持久层
持久层负责存储数据和文件,但在测试自动化中,我们可以将此层用作辅助层,其中将包含各种组件类,例如 Selenium 辅助类、数据辅助类、API 辅助类、日志记录器等。
结合Step2框架选型中的例子,继续介绍混合驱动测试(Hybrid-Driven Testing)构建四层自动化测试框架
四层架构
层级 | 作用 | 相关技术 / 工具 |
---|---|---|
测试用例层(Test Layer) | 编写测试用例,调用业务逻辑层,使用Gherkin(BDD)+ Pytest | Pytest / Cucumber / Behave |
业务层(Service Layer) | 封装业务操作(如登录、下单),提供高层 API,处理数据驱动(DDT) | Python Service Classes |
页面对象层(Page Object Layer) | 封装 Web UI 交互逻辑,隔离 UI 变化 | Selenium / Playwright / Cypress |
持久层(Persistence Layer) | 处理 WebDriver 初始化、API 交互、数据库操作、日志 | Requests / DBUtil / Logging |
测试用例层(Test Layer - BDD + Pytest)
作用:
- 采用 行为驱动测试(BDD),让测试、开发、产品经理协同编写测试。
- 使用 数据驱动测试(DDT),支持不同数据集的自动化执行。
目录结构示例
tests/
features/
login.feature
checkout.feature
step_definitions/
test_login.py
test_checkout.py
conftest.py # Pytest 配置
示例:BDD 测试用例(Gherkin 语法)
Feature: User Login
Scenario Outline: Login with multiple user credentials
Given the user is on the login page
When the user enters "<username>" and "<password>"
Then the user should be redirected to the dashboard
Examples:
| username | password |
| user1 | pass123 |
| user2 | pass456 |
业务层(Service Layer - 业务逻辑封装)
作用:
- 业务层提供高层 API,封装用户操作(如 "用户登录"、"下单")。
- 通过 数据驱动(DDT) 获取测试数据,避免测试用例代码重复。
from pages.login_page import LoginPage
from utilities.data_provider import DataProvider
class LoginService:
def __init__(self, driver):
self.login_page = LoginPage(driver)
def login_with_credentials(self, username, password):
self.login_page.enter_username(username)
self.login_page.enter_password(password)
self.login_page.click_login_button()
def login_with_test_data(self):
for user in DataProvider.get_test_data("login"):
self.login_with_credentials(user["username"], user["password"])
页面对象层(Page Object Layer - POM 设计)
作用:
- 封装 UI 交互逻辑,确保页面结构变化不会影响测试用例。
- 使用 POM 设计模式,让 UI 自动化测试可复用、易维护。
示例:LoginPage.py(Selenium)
from selenium.webdriver.common.by import By
class LoginPage:
def __init__(self, driver):
self.driver = driver
self.username_field = (By.ID, "username")
self.password_field = (By.ID, "password")
self.login_button = (By.ID, "loginBtn")
def enter_username(self, username):
self.driver.find_element(*self.username_field).send_keys(username)
def enter_password(self, password):
self.driver.find_element(*self.password_field).send_keys(password)
def click_login_button(self):
self.driver.find_element(*self.login_button).click()
持久层或底层(Persistence Layer - 数据库 / API / WebDriver)
作用:
- 数据驱动管理(DataProvider):从 JSON / Excel / DB 获取或生成测试数据。
- WebDriver管理(BrowserFactory):提供不同浏览器的实例化逻辑。
示例:数据驱动(DbUtil)
import sqlite3
class DBUtil:
def __init__(self):
self.connection = sqlite3.connect("test_data.db")
self.cursor = self.connection.cursor()
def get_login_data(self):
self.cursor.execute("SELECT username, password FROM users")
return self.cursor.fetchall()
示例:WebDriver 工厂类
from selenium import webdriver
class WebDriverFactory:
@staticmethod
def get_driver(browser="chrome", headless=False):
options = None
if browser.lower() == "chrome":
options = webdriver.ChromeOptions()
elif browser.lower() == "firefox":
options = webdriver.FirefoxOptions()
else:
raise ValueError("Unsupported browser type")
if headless:
options.add_argument("--headless")
if browser.lower() == "chrome":
return webdriver.Chrome(options=options)
elif browser.lower() == "firefox":
return webdriver.Firefox(options=options)
测试用例调用
def test_open_browser():
driver = WebDriverFactory.get_driver("chrome", headless=True)
driver.get("<https://example.com>")
assert "Example" in driver.title
driver.quit()