自动化测试框架设计之道 – 分层架构与数据驱动最佳实践

前言

大家好,我是测试架构师。从业8年,搭建过多个自动化测试框架。很多测试同行问我:”框架该怎么设计?”今天这篇文章,我想系统分享自动化测试框架的设计理念和实践经验。

一、为什么需要测试框架?

没有框架的痛点

直接写测试脚本的问题:

框架的价值

好的测试框架带来:

二、分层架构设计

三层架构模型

经典的自动化测试框架分层:


┌─────────────────────────────────────┐
│ 测试用例层 (Cases) │ 业务测试逻辑
├─────────────────────────────────────┤
│ 业务逻辑层 (Services) │ 业务操作封装
├─────────────────────────────────────┤
│ 页面对象层 (Pages) │ 元素定位、操作
├─────────────────────────────────────┤
│ 基础设施层 (Core) │ Driver、配置、工具
└─────────────────────────────────────┘

各层职责

基础设施层(Core):

页面对象层(Pages):

业务逻辑层(Services):

测试用例层(Cases):

三、框架目录结构

Python项目结构


test_framework/
├── config/ # 配置文件
│ ├── config.yaml # 主配置
│ ├── dev.yaml # 开发环境
│ └── prod.yaml # 生产环境
├── core/ # 基础设施层
│ ├── driver.py # Driver管理
│ ├── config_loader.py # 配置加载
│ ├── logger.py # 日志模块
│ ├── report.py # 报告生成
│ └── utils.py # 工具函数
├── pages/ # 页面对象层
│ ├── base_page.py # 页面基类
│ ├── login_page.py # 登录页面
│ ├── home_page.py # 首页
│ └── order_page.py # 订单页面
├── services/ # 业务逻辑层
│ ├── user_service.py # 用户业务
│ ├── order_service.py # 订单业务
│ └── auth_service.py # 认证业务
├── tests/ # 测试用例层
│ ├── test_login.py # 登录测试
│ ├── test_order.py # 订单测试
│ └── conftest.py # Pytest配置
├── data/ # 测试数据
│ ├── users.yaml # 用户数据
│ ├── orders.yaml # 订单数据
│ └── test_cases.csv # 测试数据集
├── reports/ # 测试报告
├── logs/ # 日志文件
├── requirements.txt # 依赖
└── pytest.ini # Pytest配置

四、核心模块实现

Driver管理

python

core/driver.py

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from core.config_loader import Config

class DriverManager:
“””WebDriver管理类”””

_instance = None
_driver = None

def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance

def get_driver(self):
“””获取Driver实例”””
if self._driver is None:
self._driver = self._create_driver()
return self._driver

def _create_driver(self):
“””创建Driver”””
config = Config().get(“browser”)

options = webdriver.ChromeOptions()
if config.get(“headless”):
options.add_argument(“–headless”)
options.add_argument(“–no-sandbox”)
options.add_argument(“–disable-dev-shm-usage”)

driver = webdriver.Chrome(
service=Service(ChromeDriverManager().install()),
options=options
)
driver.maximize_window()
return driver

def quit_driver(self):
“””关闭Driver”””
if self._driver:
self._driver.quit()
self._driver = None

页面基类

python

pages/base_page.py

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from core.driver import DriverManager
from core.logger import Logger

class BasePage:
“””页面基类”””

def __init__(self):
self.driver = DriverManager().get_driver()
self.logger = Logger()
self.wait = WebDriverWait(self.driver, 10)

def find_element(self, locator):
“””定位元素”””
self.logger.info(f”定位元素: {locator}”)
return self.wait.until(EC.presence_of_element_located(locator))

def click(self, locator):
“””点击元素”””
element = self.find_element(locator)
self.logger.info(f”点击元素: {locator}”)
element.click()

def input_text(self, locator, text):
“””输入文本”””
element = self.find_element(locator)
self.logger.info(f”输入文本: {text}”)
element.clear()
element.send_keys(text)

def get_text(self, locator):
“””获取文本”””
element = self.find_element(locator)
return element.text

def is_visible(self, locator):
“””判断元素可见”””
try:
self.wait.until(EC.visibility_of_element_located(locator))
return True
except:
return False

业务服务类

python

services/auth_service.py

from pages.login_page import LoginPage
from pages.home_page import HomePage
from core.logger import Logger

class AuthService:
“””认证业务服务”””

def __init__(self):
self.login_page = LoginPage()
self.home_page = HomePage()
self.logger = Logger()

def login(self, username, password):
“””登录流程”””
self.logger.info(f”登录: {username}”)

self.login_page.open()
self.login_page.input_username(username)
self.login_page.input_password(password)
self.login_page.click_login()

return self.home_page.is_logged_in()

def logout(self):
“””退出登录”””
self.logger.info(“退出登录”)
self.home_page.click_logout()

五、数据驱动设计

数据源类型

框架支持多种数据源:

数据源 格式 用途
YAML 配置数据 环境配置、测试参数
CSV 表格数据 批量测试数据
JSON 结构化数据 复杂测试数据
Excel 表格数据 业务数据

YAML数据管理

yaml

data/users.yaml

users:
valid_user:
username: “testuser”
password: “password123”
expected: “success”

invalid_user:
username: “wronguser”
password: “wrongpass”
expected: “failure”

empty_user:
username: “”
password: “”
expected: “validation_error”

CSV数据驱动

python

tests/test_login.py

import pytest
import csv

def load_test_data():
“””加载CSV测试数据”””
with open(“data/test_cases.csv”, “r”) as f:
reader = csv.DictReader(f)
return list(reader)

@pytest.mark.parametrize(“data”, load_test_data())
def test_login(data):
“””数据驱动登录测试”””
service = AuthService()
result = service.login(data[“username”], data[“password”])
assert result == (data[“expected”] == “success”)

六、配置管理

配置文件设计

yaml

config/config.yaml

browser:
name: chrome
headless: false
window_size: maximize

test:
timeout: 10
retry: 3
screenshot_on_fail: true

report:
format: allure
output_dir: reports/

environment:
base_url: http://test.example.com
api_url: http://api.test.example.com

配置加载器

python

core/config_loader.py

import yaml
from pathlib import Path

class Config:
“””配置管理”””

_instance = None
_config = None

def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._load_config()
return cls._instance

def _load_config(self):
“””加载配置”””
config_path = Path(“config/config.yaml”)
with open(config_path, “r”) as f:
self._config = yaml.safe_load(f)

def get(self, key, default=None):
“””获取配置”””
keys = key.split(“.”)
value = self._config
for k in keys:
if isinstance(value, dict):
value = value.get(k)
else:
return default
return value if value else default

七、日志与报告

日志模块

python

core/logger.py

import logging
from datetime import datetime

class Logger:
“””日志管理”””

def __init__(self, name=”test”):
self.logger = logging.getLogger(name)
self.logger.setLevel(logging.DEBUG)

# 文件Handler
file_handler = logging.FileHandler(
f”logs/test_{datetime.now().strftime(‘%Y%m%d’)}.log”
)
file_handler.setLevel(logging.DEBUG)

# 控制台Handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)

# 格式化
formatter = logging.Formatter(
“%(asctime)s – %(name)s – %(levelname)s – %(message)s”
)
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)

self.logger.addHandler(file_handler)
self.logger.addHandler(console_handler)

def info(self, msg):
self.logger.info(msg)

def error(self, msg):
self.logger.error(msg)

def debug(self, msg):
self.logger.debug(msg)

八、框架最佳实践

单例模式应用

Driver、Config、Logger使用单例模式,避免重复创建。

Page Object原则

业务逻辑封装

测试用例规范

总结

自动化测试框架设计要点:

要点 内容
分层架构 Cases –

Services -> Pages -> Core

| Driver管理 | 单例模式、自动初始化 |
| Page Object | 元素定位、操作封装 |
| 数据驱动 | YAML/CSV/JSON数据源 |
| 配置管理 | 环境隔离、参数配置 |
| 日志报告 | 日志记录、报告生成 |

下期预告:Jenkins测试流水线搭建 – CI/CD中的自动化测试集成实战

分享到:

探索 RunnerGo 全栈测试平台

RunnerGo 是一款面向企业的全栈测试平台,集接口测试、自动化测试、性能测试、UI测试于一体,助力企业提升研发效能。

接口测试
性能测试
自动化测试
免费体验 RunnerGo