Python自动化测试框架:unittest、pytest、Selenium、requests和Pytest-BDD

 更新时间:2026年05月02日 11:32:17   作者:第一程序员  
本文介绍了Python中几种常见的自动化测试框架,包括unittest、pytest、Selenium、requests和 Pytest-BDD,以及各自的优缺点和适用场景,通过实战案例帮助开发者编写高质量的测试代码,提高代码质量

前言

最近在学习 Python 的过程中,我深刻认识到自动化测试的重要性。良好的测试可以帮助我们发现代码中的错误,提高代码质量,减少回归 bug,同时也可以让我们更自信地进行代码重构。Python 拥有丰富的自动化测试框架,如 unittest、pytest、selenium 等,这些框架各有特点,适用于不同的测试场景。今天就来分享一下我的 Python 自动化测试框架实战经验,希望能帮到和我一样的萌新们。

常见的 Python 自动化测试框架

1. unittest

unittest 是 Python 标准库中的测试框架,它提供了完整的测试工具集,包括测试发现、测试套件、测试运行器等。

优点

  • 是 Python 标准库的一部分,不需要额外安装
  • 提供了完整的测试框架功能
  • 支持测试发现和测试套件
  • 支持 setUp 和 tearDown 方法

缺点

  • 语法相对繁琐,需要编写较多的 boilerplate 代码
  • 断言方法有限
  • 不支持参数化测试

适用场景

  • 简单的单元测试
  • 对测试框架依赖要求较低的项目
  • 学习测试基础概念

示例

import unittest

class TestStringMethods(unittest.TestCase):
    def setUp(self):
        # 测试前的准备工作
        self.test_string = "hello world"
    
    def tearDown(self):
        # 测试后的清理工作
        pass
    
    def test_upper(self):
        self.assertEqual(self.test_string.upper(), "HELLO WORLD")
    
    def test_isupper(self):
        self.assertFalse(self.test_string.isupper())
        self.assertTrue("HELLO".isupper())
    
    def test_split(self):
        s = "hello world"
        self.assertEqual(s.split(), ["hello", "world"])
        # 检查分割后的结果是否符合预期
        with self.assertRaises(TypeError):
            s.split(2)

if __name__ == '__main__':
    unittest.main()

2. pytest

pytest 是一个功能强大的第三方测试框架,它提供了简洁的语法、丰富的插件生态和强大的测试功能。

优点

  • 语法简洁,不需要继承特定的测试类
  • 支持参数化测试
  • 支持 fixtures 进行测试准备和清理
  • 丰富的插件生态
  • 强大的断言能力
  • 支持测试发现和测试过滤

缺点

  • 需要额外安装
  • 对于初学者来说,插件体系可能有些复杂

适用场景

  • 单元测试
  • 集成测试
  • 功能测试
  • 需要复杂测试场景的项目

示例

import pytest

def test_upper():
    assert "hello world".upper() == "HELLO WORLD"

def test_isupper():
    assert not "hello world".isupper()
    assert "HELLO".isupper()

def test_split():
    s = "hello world"
    assert s.split() == ["hello", "world"]
    with pytest.raises(TypeError):
        s.split(2)

# 参数化测试
@pytest.mark.parametrize("input, expected", [
    ("hello", "HELLO"),
    ("world", "WORLD"),
    ("test", "TEST"),
])
def test_parametrized_upper(input, expected):
    assert input.upper() == expected

# fixtures
def pytest.fixture
def setup_test_string():
    return "hello world"

def test_with_fixture(setup_test_string):
    assert setup_test_string.upper() == "HELLO WORLD"

3. Selenium

Selenium 是一个用于 Web 应用自动化测试的工具,它可以模拟用户在浏览器中的操作。

优点

  • 支持多种浏览器
  • 可以模拟真实用户操作
  • 支持多种编程语言
  • 强大的元素定位能力

缺点

  • 运行速度较慢
  • 依赖浏览器和 WebDriver
  • 容易受到页面结构变化的影响

适用场景

  • Web 应用的功能测试
  • 端到端测试
  • 用户界面测试

示例

from selenium import webdriver
from selenium.webdriver.common.by import By
import time

def test_google_search():
    # 初始化浏览器
    driver = webdriver.Chrome()
    
    try:
        # 打开 Google
        driver.get("https://www.google.com")
        
        # 定位搜索框并输入搜索内容
        search_box = driver.find_element(By.NAME, "q")
        search_box.send_keys("Python automation testing")
        search_box.submit()
        
        # 等待搜索结果加载
        time.sleep(2)
        
        # 验证搜索结果
        assert "Python automation testing" in driver.title
        
        # 定位搜索结果链接
        search_results = driver.find_elements(By.CSS_SELECTOR, "h3")
        assert len(search_results) > 0
        
    finally:
        # 关闭浏览器
        driver.quit()

4. Requests

Requests 是一个用于 HTTP 请求的库,它可以用于 API 测试。

优点

  • 语法简洁,使用方便
  • 支持各种 HTTP 方法
  • 支持会话管理
  • 支持文件上传和下载

缺点

  • 只适用于 API 测试
  • 不支持浏览器操作

适用场景

  • RESTful API 测试
  • HTTP 服务测试
  • 接口测试

示例

import requests

def test_get_request():
    response = requests.get("https://httpbin.org/get")
    assert response.status_code == 200
    assert "args" in response.json()

def test_post_request():
    data = {"name": "test", "value": 123}
    response = requests.post("https://httpbin.org/post", json=data)
    assert response.status_code == 200
    assert response.json()["json"] == data

def test_put_request():
    data = {"name": "updated", "value": 456}
    response = requests.put("https://httpbin.org/put", json=data)
    assert response.status_code == 200
    assert response.json()["json"] == data

def test_delete_request():
    response = requests.delete("https://httpbin.org/delete")
    assert response.status_code == 200

5. Pytest-BDD

Pytest-BDD 是基于行为驱动开发(BDD)的测试框架,它使用 Gherkin 语言编写测试场景。

优点

  • 测试场景使用自然语言描述,易于理解
  • 支持 Given-When-Then 语法
  • 与 pytest 集成,支持 pytest 的所有功能

缺点

  • 需要学习 Gherkin 语法
  • 对于简单测试来说可能有些繁琐

适用场景

  • 行为驱动开发
  • 需求驱动的测试
  • 团队协作测试

示例

# features/example.feature
"""
Feature: Example feature
    Scenario: Example scenario
        Given I have a string "hello"
        When I uppercase the string
        Then the result should be "HELLO"
"""

# tests/test_example.py
from pytest_bdd import scenario, given, when, then

@scenario('features/example.feature', 'Example scenario')
def test_example():
    pass

@given('I have a string "hello"')
def have_string():
    return "hello"

@when('I uppercase the string')
def uppercase_string(have_string):
    return have_string.upper()

@then('the result should be "HELLO"')
def check_result(uppercase_string):
    assert uppercase_string == "HELLO"

实战案例:使用 pytest 进行单元测试

需求分析

我们将创建一个简单的计算器类,并使用 pytest 对其进行单元测试。

实现步骤

  1. 创建计算器类:实现基本的算术操作
  2. 编写测试用例:测试计算器的各种功能
  3. 运行测试:使用 pytest 运行测试并查看结果
  4. 分析测试结果:根据测试结果调整代码

代码实现

计算器类

# calculator.py
class Calculator:
    def add(self, a, b):
        """加法操作"""
        return a + b
    
    def subtract(self, a, b):
        """减法操作"""
        return a - b
    
    def multiply(self, a, b):
        """乘法操作"""
        return a * b
    
    def divide(self, a, b):
        """除法操作"""
        if b == 0:
            raise ValueError("除数不能为零")
        return a / b

测试用例

# test_calculator.py
import pytest
from calculator import Calculator

@pytest.fixture
def calculator():
    return Calculator()

def test_add(calculator):
    assert calculator.add(1, 2) == 3
    assert calculator.add(-1, 1) == 0
    assert calculator.add(0, 0) == 0
    assert calculator.add(1.5, 2.5) == 4.0

def test_subtract(calculator):
    assert calculator.subtract(5, 3) == 2
    assert calculator.subtract(1, 5) == -4
    assert calculator.subtract(0, 0) == 0
    assert calculator.subtract(4.5, 2.5) == 2.0

def test_multiply(calculator):
    assert calculator.multiply(2, 3) == 6
    assert calculator.multiply(-1, 5) == -5
    assert calculator.multiply(0, 100) == 0
    assert calculator.multiply(2.5, 4) == 10.0

def test_divide(calculator):
    assert calculator.divide(10, 2) == 5
    assert calculator.divide(5, 2) == 2.5
    assert calculator.divide(-10, 2) == -5
    with pytest.raises(ValueError, match="除数不能为零"):
        calculator.divide(10, 0)

# 参数化测试
@pytest.mark.parametrize("a, b, expected", [
    (1, 2, 3),
    (-1, 1, 0),
    (0, 0, 0),
    (1.5, 2.5, 4.0),
])
def test_parametrized_add(calculator, a, b, expected):
    assert calculator.add(a, b) == expected

运行测试

# 安装 pytest
pip install pytest

# 运行测试
pytest test_calculator.py -v

# 运行特定测试
pytest test_calculator.py::test_add -v

# 运行带有特定标记的测试
pytest test_calculator.py -m slow -v

# 生成测试报告
pytest test_calculator.py --html=report.html

实战案例:使用 Selenium 进行 Web 自动化测试

需求分析

我们将使用 Selenium 测试一个简单的登录功能,包括输入用户名和密码,点击登录按钮,验证登录是否成功。

实现步骤

  1. 安装 Selenium:安装 Selenium 和 WebDriver
  2. 编写测试用例:测试登录功能
  3. 运行测试:使用 pytest 运行测试并查看结果
  4. 分析测试结果:根据测试结果调整测试用例

代码实现

测试用例

# test_login.py
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
import pytest

@pytest.fixture
def driver():
    # 初始化浏览器
    driver = webdriver.Chrome()
    driver.implicitly_wait(10)
    yield driver
    # 测试结束后关闭浏览器
    driver.quit()

def test_login_success(driver):
    # 打开登录页面
    driver.get("https://example.com/login")
    
    # 输入用户名和密码
    username_input = driver.find_element(By.ID, "username")
    password_input = driver.find_element(By.ID, "password")
    username_input.send_keys("testuser")
    password_input.send_keys("testpassword")
    
    # 点击登录按钮
    login_button = driver.find_element(By.ID, "login-button")
    login_button.click()
    
    # 等待登录成功页面加载
    WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.ID, "welcome-message"))
    )
    
    # 验证登录成功
    welcome_message = driver.find_element(By.ID, "welcome-message")
    assert "Welcome" in welcome_message.text

def test_login_failure(driver):
    # 打开登录页面
    driver.get("https://example.com/login")
    
    # 输入错误的用户名和密码
    username_input = driver.find_element(By.ID, "username")
    password_input = driver.find_element(By.ID, "password")
    username_input.send_keys("testuser")
    password_input.send_keys("wrongpassword")
    
    # 点击登录按钮
    login_button = driver.find_element(By.ID, "login-button")
    login_button.click()
    
    # 等待错误消息显示
    WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.ID, "error-message"))
    )
    
    # 验证错误消息
    error_message = driver.find_element(By.ID, "error-message")
    assert "Invalid username or password" in error_message.text

运行测试

# 安装 Selenium
pip install selenium

# 下载 WebDriver(ChromeDriver)并添加到 PATH

# 运行测试
pytest test_login.py -v

实战案例:使用 Requests 进行 API 测试

需求分析

我们将使用 Requests 测试一个简单的 RESTful API,包括 GET、POST、PUT 和 DELETE 请求。

实现步骤

  1. 安装 Requests:安装 Requests 库
  2. 编写测试用例:测试 API 的各种功能
  3. 运行测试:使用 pytest 运行测试并查看结果
  4. 分析测试结果:根据测试结果调整测试用例

代码实现

测试用例

# test_api.py
import requests
import pytest

BASE_URL = "https://jsonplaceholder.typicode.com"

def test_get_posts():
    """测试获取帖子列表"""
    response = requests.get(f"{BASE_URL}/posts")
    assert response.status_code == 200
    posts = response.json()
    assert isinstance(posts, list)
    assert len(posts) > 0

def test_get_post():
    """测试获取单个帖子"""
    post_id = 1
    response = requests.get(f"{BASE_URL}/posts/{post_id}")
    assert response.status_code == 200
    post = response.json()
    assert post["id"] == post_id

def test_create_post():
    """测试创建帖子"""
    data = {
        "title": "Test Post",
        "body": "This is a test post",
        "userId": 1
    }
    response = requests.post(f"{BASE_URL}/posts", json=data)
    assert response.status_code == 201
    post = response.json()
    assert post["title"] == data["title"]
    assert post["body"] == data["body"]
    assert post["userId"] == data["userId"]

def test_update_post():
    """测试更新帖子"""
    post_id = 1
    data = {
        "title": "Updated Test Post",
        "body": "This is an updated test post",
        "userId": 1
    }
    response = requests.put(f"{BASE_URL}/posts/{post_id}", json=data)
    assert response.status_code == 200
    post = response.json()
    assert post["id"] == post_id
    assert post["title"] == data["title"]
    assert post["body"] == data["body"]

def test_delete_post():
    """测试删除帖子"""
    post_id = 1
    response = requests.delete(f"{BASE_URL}/posts/{post_id}")
    assert response.status_code == 200

运行测试

# 安装 Requests
pip install requests

# 运行测试
pytest test_api.py -v

测试最佳实践

  1. 测试隔离:每个测试应该独立运行,不依赖于其他测试的状态

  2. 测试覆盖:确保测试覆盖主要的功能和边界情况

  3. 测试命名:使用清晰、描述性的测试名称

  4. 测试数据:使用合适的测试数据,包括正常情况和边界情况

  5. 测试速度:保持测试运行速度快,便于频繁运行

  6. 测试维护:定期更新测试,确保测试与代码同步

  7. 测试报告:生成详细的测试报告,便于分析测试结果

  8. 持续集成:在持续集成系统中运行测试,确保代码质量

常见问题与解决方案

1. 测试运行慢

问题:测试运行速度慢,影响开发效率。

解决方案

  • 减少测试中的网络请求和数据库操作
  • 使用 mock 模拟外部依赖
  • 并行运行测试
  • 只运行相关的测试

2. 测试不稳定

问题:测试有时通过,有时失败,不稳定。

解决方案

  • 确保测试环境一致
  • 避免测试之间的依赖
  • 处理测试中的异步操作
  • 增加适当的等待时间

3. 测试代码维护困难

问题:测试代码难以维护,随着代码的变化需要频繁更新。

解决方案

  • 使用 fixtures 减少重复代码
  • 采用页面对象模式(Page Object Pattern)管理 UI 测试
  • 保持测试代码简洁明了
  • 定期重构测试代码

4. 测试覆盖不足

问题:测试覆盖不足,无法发现所有的 bug。

解决方案

  • 使用测试覆盖工具(如 coverage.py)分析覆盖情况
  • 针对边界情况和异常情况编写测试
  • 定期审查测试覆盖报告

总结

Python 拥有丰富的自动化测试框架,每个框架都有其特点和适用场景。unittest 是 Python 标准库的一部分,适合简单的单元测试;pytest 提供了简洁的语法和丰富的功能,适合各种测试场景;Selenium 用于 Web 应用的自动化测试;Requests 用于 API 测试;Pytest-BDD 用于行为驱动开发。

通过使用这些测试框架,我们可以编写高质量的测试代码,提高代码质量,减少回归 bug,同时也可以让我们更自信地进行代码重构。作为一个从后端转 Rust 的萌新,我在学习 Python 自动化测试的过程中遇到了一些挑战,也学到了很多东西。通过不断学习和实践,我相信我能够编写更加有效的测试代码。

到此这篇关于Python自动化测试框架:unittest、pytest、Selenium、requests和Pytest-BDD的文章就介绍到这了,更多相关Python自动化测试框架项目实战内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Python3中的re.findall()方法及re.compile()

    Python3中的re.findall()方法及re.compile()

    这篇文章主要介绍了Python3中的re.findall()方法及re.compile(),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-05-05
  • python文件操作之批量修改文件后缀名的方法

    python文件操作之批量修改文件后缀名的方法

    这篇文章主要介绍了python文件操作之批量修改文件后缀名,需要的朋友可以参考下
    2018-08-08
  • Python selenium的基本使用方法分析

    Python selenium的基本使用方法分析

    这篇文章主要介绍了Python selenium的基本使用方法,结合实例形式分析了Python使用selenium模块进行web自动化测试的基本模块导入、操作技巧与相关注意事项,需要的朋友可以参考下
    2019-12-12
  • python版本单链表实现代码

    python版本单链表实现代码

    这篇文章主要为大家详细介绍了python版本单链表实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-09-09
  • 实用自动化运维Python脚本分享

    实用自动化运维Python脚本分享

    今天小编就为大家分享一篇实用自动化运维Python脚本。具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-06-06
  • Python中的Joblib库使用学习总结

    Python中的Joblib库使用学习总结

    这篇文章主要介绍了Python中的Joblib库使用学习总结,Joblib是一组在Python中提供轻量级流水线的工具,Joblib已被优化得很快速,很健壮了,特别是在大数据上,并对numpy数组进行了特定的优化,需要的朋友可以参考下
    2023-08-08
  • PyQt5+Caffe+Opencv搭建人脸识别登录界面

    PyQt5+Caffe+Opencv搭建人脸识别登录界面

    这篇文章主要为大家详细介绍了PyQt5+Caffe+Opencv搭建人脸识别登录界面,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-08-08
  • Python项目打包与发布的三大工具(setuptools、Poetry、Flit)使用完全指南

    Python项目打包与发布的三大工具(setuptools、Poetry、Flit)使用完全指南

    这篇文章全面介绍了Python打包与发布的三大工具,包括setuptools、Poetry和Flit,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2026-03-03
  • python实现在线翻译

    python实现在线翻译

    这篇文章主要介绍了python实现在线翻译,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-06-06
  • Python脚本实现给图片添加安全斜向水印

    Python脚本实现给图片添加安全斜向水印

    在日常工作中,证件照片的安全性至关重要,本文和大家分享了一个Python 脚本,用于给图片添加斜向平铺水印,有效防止信息泄露或滥用,希望对大家有所帮助
    2025-08-08

最新评论