如何编写自动化测试脚本

wen 实用脚本 2

本文目录导读:

如何编写自动化测试脚本

  1. 测试框架选择
  2. 编写测试脚本的基本结构
  3. 测试脚本设计模式
  4. 等待策略
  5. 断言的使用
  6. 测试报告生成
  7. 最佳实践
  8. CI/CD集成
  9. 常见问题处理

我来介绍如何系统地编写自动化测试脚本,涵盖从基础到进阶的实践方法。

测试框架选择

主流框架对比

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

这些是编写自动化测试脚本的核心技能,建议从简单的项目开始,逐步应用到复杂的测试场景中,记得保持测试代码的维护性和可扩展性。

抱歉,评论功能暂时关闭!