什么是自动化测试框架?

框架并非仅仅是引入一些依赖库、合并几个类。它是一个综合的解决方案,涉及设计模式、模块化、可重用性等方面。

自动化测试框架的目的是提供一套标准和规则、从而使自动化测试的开发和维护更高效、更易于扩展

构建合适的框架至关重要,使用结构良好的框架可以提高团队效率,提高测试准确性,最大限度地提高测试覆盖率,降低成本和维护费用——最终带来更高的投资回报率 (ROI)。


如何从零开始构建自动化测试框架

简单的说,构建自动化测试框架需要将框架视作一个应用或产品一样去对待,构建自动化测试框架分为以下步骤:

  • 需求分析和调研
  • 框架选型和决策
  • 框架设计

Step1 - 需求分析和调研

在需求分析和调研阶段,除了明确测试对象和框架的使用场景,还需要深入了解和评估框架的潜在用户(如开发人员、测试人员以及CI/CD工具)的需求。以下是一些关键问题,可以帮助更全面地定义框架的需求:

  1. 框架的适用性和覆盖范围

    1. 框架是否需要支持跨平台测试(如Web、移动端、桌面端)?
    2. 框架主要覆盖哪些测试用例和测试场景(功能测试、性能测试、回归测试等)?
    3. 哪些用例是业务价值较高、需要优先自动化的?
  2. 技术依赖与交互

    • 测试时是否需要与数据库或外部接口交互?这些交互是否需要模拟环境或使用真实数据?
    • 框架是否需要支持特定的技术栈或与现有工具(如Jenkins、GitLab CI等)集成?
  3. 可扩展性与灵活性

    • 框架未来可能面临哪些变化和扩展需求(如支持新的测试场景、新技术或更高的并发量)?
    • 需要支持的测试用例数量是多少?框架是否需要设计为可扩展以满足未来增长需求?
  4. 使用者分析

    • 框架的主要使用和维护人员是谁?他们的技术背景和能力如何(如开发人员是否擅长编程,测试人员是否熟悉框架工具)?
    • 是否需要提供友好的界面或详细的文档以降低框架的使用门槛?
  5. 测试驱动类型

    在不同项目中,测试框架可能需要支持不同的测试驱动方式,选择合适的驱动方式可以提高测试的可维护性、可读性和可扩展性。以下是几种常见的测试驱动类型,后面将在选型决策部分详细介绍其适用场景和工具选择:

    • 数据驱动测试(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流程集成。

团队情况

  1. 开发语言主要是 Python JavaScript
  2. 团队中测试人员对自动化测试的经验不一,其中一部分成员熟悉 Selenium,对Cypress 和 Playwright接触较少
  3. 项目时间紧张,需要快速迭代和测试反馈。

需求分析

  • 浏览器支持:需要覆盖 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) 获取测试数据,避免测试用例代码重复。

示例:LoginService.py


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()

 

Logo

一站式 AI 云服务平台

更多推荐