本文目录导读:

我来介绍如何系统地编写自动化测试脚本,涵盖从基础到进阶的实践方法。
测试框架选择
主流框架对比
# Python - Pytest: 简单灵活,插件丰富 - Unittest: Python内置,无需额外安装 - Robot Framework: 关键字驱动,易读性高 # JavaScript/TypeScript - Jest: React生态首选 - Playwright: 现代Web测试利器 - Cypress: 开发者友好,调试方便 # Java - Selenium: 老牌Web测试工具 - TestNG: 功能强大,支持并行 - JUnit: Java标准测试框架
编写测试脚本的基本结构
简单的Pytest测试示例
# test_login.py
import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class TestLogin:
"""登录功能测试"""
def setup_method(self):
"""每个测试方法执行前的准备"""
self.driver = webdriver.Chrome()
self.driver.get("https://example.com")
self.wait = WebDriverWait(self.driver, 10)
def teardown_method(self):
"""每个测试方法执行后的清理"""
self.driver.quit()
def test_valid_login(self):
"""测试有效登录"""
# 定位元素
username = self.driver.find_element(By.ID, "username")
password = self.driver.find_element(By.ID, "password")
submit_btn = self.driver.find_element(By.CSS_SELECTOR, "button[type='submit']")
# 执行操作
username.send_keys("test_user")
password.send_keys("password123")
submit_btn.click()
# 断言结果
welcome_msg = self.wait.until(
EC.presence_of_element_located((By.CLASS_NAME, "welcome"))
)
assert "欢迎" in welcome_msg.text
def test_invalid_login(self):
"""测试无效登录"""
username = self.driver.find_element(By.ID, "username")
password = self.driver.find_element(By.ID, "password")
username.send_keys("wrong_user")
password.send_keys("wrong_password")
error_msg = self.driver.find_element(By.CLASS_NAME, "error")
assert "用户名或密码错误" in error_msg.text
测试脚本设计模式
Page Object Model(POM)
# pages/base_page.py
class BasePage:
"""所有页面的基类"""
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
def find_element(self, locator):
return self.wait.until(
EC.presence_of_element_located(locator)
)
def click(self, locator):
element = self.find_element(locator)
element.click()
def input_text(self, locator, text):
element = self.find_element(locator)
element.clear()
element.send_keys(text)
# pages/login_page.py
class LoginPage(BasePage):
"""登录页面"""
# 定位器
USERNAME = (By.ID, "username")
PASSWORD = (By.ID, "password")
SUBMIT = (By.CSS_SELECTOR, "button[type='submit']")
ERROR_MSG = (By.CLASS_NAME, "error")
def login(self, username, password):
"""执行登录操作"""
self.input_text(self.USERNAME, username)
self.input_text(self.PASSWORD, password)
self.click(self.SUBMIT)
def get_error_message(self):
"""获取错误信息"""
return self.find_element(self.ERROR_MSG).text
数据驱动测试
# test_data.json
{
"valid_cases": [
{
"username": "user1",
"password": "pass123",
"expected": "登录成功"
},
{
"username": "admin",
"password": "admin123",
"expected": "欢迎管理员"
}
],
"invalid_cases": [
{
"username": "",
"password": "",
"expected": "请输入用户名"
}
]
}
# 使用参数化
import pytest
import json
class TestLoginDataDriven:
@pytest.fixture
def test_data(self):
with open('test_data.json', 'r') as f:
return json.load(f)
@pytest.mark.parametrize("case", [
{"username": "user1", "password": "pass123", "expected": "登录成功"},
{"username": "admin", "password": "admin123", "expected": "欢迎管理员"}
])
def test_valid_login_cases(self, case):
# 测试用例逻辑
assert login(case["username"], case["password"]) == case["expected"]
等待策略
显式等待
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# 等待特定条件
element = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.ID, "submit_button"))
)
# 等待页面加载完成
WebDriverWait(driver, 20).until(
EC.presence_of_element_located((By.TAG_NAME, "body"))
)
# 等待Ajax请求完成
WebDriverWait(driver, 10).until(
lambda driver: driver.execute_script("return jQuery.active == 0")
)
隐式等待
# 设置全局等待时间 driver.implicitly_wait(10) # 10秒
断言的使用
Pytest断言
import pytest
def test_assertions():
# 基本断言
assert result == expected
assert result != expected
assert condition is True
assert value is not None
# 集合断言
assert "substring" in text
assert 1 in [1, 2, 3]
assert {"key": "value"} in list_of_dicts
# 数值断言
assert abs(calculated - expected) < tolerance
# 自定义错误消息
assert condition, f"Condition failed with {variable}"
# 异常断言
with pytest.raises(ValueError) as exc_info:
function_that_raises()
assert "error message" in str(exc_info.value)
测试报告生成
Pytest-html
# conftest.py
import pytest
import os
from datetime import datetime
@pytest.hookimpl(tryfirst=True)
def pytest_configure(config):
"""配置报告"""
if not os.path.exists('reports'):
os.makedirs('reports')
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
config.option.htmlpath = f'reports/report_{timestamp}.html'
# 运行命令
pytest test_login.py --html=reports/report.html --self-contained-html
Allure报告
# test_login.py
import allure
@allure.feature("用户登录")
class TestLogin:
@allure.story("正常登录")
@allure.severity(allure.severity_level.CRITICAL)
def test_valid_login(self):
with allure.step("输入用户名"):
username.send_keys("test_user")
with allure.step("输入密码"):
password.send_keys("password123")
with allure.step("点击登录按钮"):
submit.click()
with allure.step("验证登录成功"):
assert welcome_message.is_displayed()
# 运行命令
pytest --alluredir=./allure-results
allure serve ./allure-results
最佳实践
测试脚本组织架构
tests/
├── conftest.py # 共享fixtures
├── pages/ # 页面对象
│ ├── __init__.py
│ ├── base_page.py
│ └── login_page.py
├── test_cases/ # 测试用例
│ ├── __init__.py
│ ├── test_login.py
│ └── test_register.py
├── data/ # 测试数据
│ ├── test_data.json
│ └── test_data.yaml
├── utils/ # 工具函数
│ ├── __init__.py
│ ├── logger.py
│ └── db_utils.py
├── config/ # 配置文件
│ └── config.ini
└── reports/ # 测试报告
关键原则
# 1. 独立性
@pytest.fixture
def unique_data():
"""每个测试用例使用唯一数据"""
timestamp = int(time.time() * 1000)
return f"test_user_{timestamp}"
# 2. 可重复性
def test_repeatable():
"""测试不依赖外部状态"""
for _ in range(3):
result = function_being_tested()
assert result == expected
# 3. 可读性
def test_user_can_purchase_item():
"""描述清晰的测试名称"""
# Given: 用户已登录且有商品
# When: 用户点击购买
# Then: 订单被创建
pass
# 4. 错误捕获
def test_with_retry():
"""重试机制"""
@retry(max_attempts=3, wait=1)
def flaky_operation():
return operation_may_fail()
CI/CD集成
Jenkins配置
pipeline {
agent any
stages {
stage('Checkout') {
steps {
git 'https://github.com/your/repo.git'
}
}
stage('Test') {
steps {
sh 'pip install -r requirements.txt'
sh 'pytest tests/ --junitxml=report.xml'
}
}
stage('Reports') {
steps {
junit 'report.xml'
}
}
}
}
GitHub Actions
name: Automated Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install dependencies
run: |
pip install -r requirements.txt
- name: Run tests
run: |
pytest tests/ --html=report.html
- name: Upload artifact
uses: actions/upload-artifact@v2
with:
name: test-report
path: report.html
常见问题处理
元素定位问题
def wait_for_element(driver, locator, timeout=10):
"""智能等待元素"""
try:
element = WebDriverWait(driver, timeout).until(
EC.presence_of_element_located(locator)
)
return element
except TimeoutException:
# 截图方便调试
driver.save_screenshot(f"debug_{str(locator)}.png")
raise
# 多策略定位
def find_element_flexible(driver, strategies):
"""尝试多种定位策略"""
for strategy in strategies:
try:
return driver.find_element(*strategy)
except NoSuchElementException:
continue
raise NoSuchElementException("所有定位策略都失败")
异步处理
import asyncio
from playwright.async_api import async_playwright
async def test_async_flow():
async with async_playwright() as p:
browser = await p.chromium.launch()
page = await browser.new_page()
# 异步等待
await page.wait_for_load_state('networkidle')
# 并行执行
async with asyncio.TaskGroup() as tg:
task1 = tg.create_task(page.locator("#element1").click())
task2 = tg.create_task(page.locator("#element2").click())
await browser.close()
这些是编写自动化测试脚本的核心技能,建议从简单的项目开始,逐步应用到复杂的测试场景中,记得保持测试代码的维护性和可扩展性。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播