跳转到内容

单元测试编写

来自代码酷
Admin留言 | 贡献2025年5月12日 (一) 00:26的版本 (Page creation by admin bot)

(差异) ←上一版本 | 已核准修订 (差异) | 最后版本 (差异) | 下一版本→ (差异)

单元测试编写[编辑 | 编辑源代码]

简介[编辑 | 编辑源代码]

单元测试是软件开发中的一种测试方法,用于验证代码中最小的可测试单元(如函数、方法或类)是否按预期工作。它通常是自动化测试的一部分,帮助开发者快速发现和修复代码中的错误,提高代码质量,并支持持续集成(CI)流程。

单元测试的核心特点包括:

  • 隔离性:每个测试应独立运行,不依赖外部资源(如数据库、网络)。
  • 快速执行:测试应能在短时间内完成,以便频繁运行。
  • 确定性:相同输入应始终产生相同输出。

单元测试的基本结构[编辑 | 编辑源代码]

一个典型的单元测试包含以下部分: 1. 准备(Arrange):设置测试环境和输入数据。 2. 执行(Act):调用被测代码。 3. 断言(Assert):验证输出是否符合预期。

示例(Python + pytest)[编辑 | 编辑源代码]

# 被测函数
def add(a: int, b: int) -> int:
    return a + b

# 测试代码
def test_add():
    # Arrange
    x, y = 2, 3
    
    # Act
    result = add(x, y)
    
    # Assert
    assert result == 5

单元测试框架[编辑 | 编辑源代码]

不同语言有各自的单元测试框架:

语言 常用框架
Python pytest, unittest
Java JUnit, TestNG
JavaScript Jest, Mocha
C# NUnit, xUnit

测试替身(Test Doubles)[编辑 | 编辑源代码]

当被测代码依赖外部组件时,可以使用测试替身模拟这些依赖:

classDiagram class Database { +get_data() List } class Service { -db: Database +process_data() List } class MockDatabase { +get_data() List } Service --> Database Service ..|> MockDatabase : 测试时替换

常见测试替身类型:

  • Mock:预设行为的对象,验证调用方式
  • Stub:提供固定响应的简单替代
  • Fake:轻量级的功能实现(如内存数据库)

Mock示例(Python + unittest.mock)[编辑 | 编辑源代码]

from unittest.mock import Mock

def test_process_data():
    # 创建mock数据库
    mock_db = Mock()
    mock_db.get_data.return_value = [1, 2, 3]
    
    service = Service(mock_db)
    result = service.process_data()
    
    assert result == [1, 2, 3]
    mock_db.get_data.assert_called_once()  # 验证调用

测试覆盖率[编辑 | 编辑源代码]

测试覆盖率衡量被测试执行的代码比例,常用指标包括:

  • 行覆盖率
  • 分支覆盖率
  • 函数覆盖率

数学表示为: 覆盖率=已执行代码可执行代码×100%

最佳实践[编辑 | 编辑源代码]

1. 测试命名:使用描述性名称(如test_add_positive_numbers) 2. 单一职责:每个测试只验证一个行为 3. FIRST原则

  * Fast(快速)
  * Isolated(隔离)
  * Repeatable(可重复)
  * Self-validating(自验证)
  * Timely(及时)

4. 避免测试实现细节:测试行为而非内部实现

实际案例[编辑 | 编辑源代码]

用户注册服务测试[编辑 | 编辑源代码]

测试一个用户注册服务,需要验证: 1. 成功注册时返回用户ID 2. 重复用户名时抛出异常 3. 密码加密存储

import pytest
from unittest.mock import Mock

def test_register_success():
    # Arrange
    user_repo = Mock()
    user_repo.exists.return_value = False
    user_repo.save.return_value = "user123"
    service = UserService(user_repo)
    
    # Act
    user_id = service.register("new@example.com", "password")
    
    # Assert
    assert user_id == "user123"
    user_repo.exists.assert_called_with("new@example.com")
    user_repo.save.assert_called()

def test_register_duplicate():
    user_repo = Mock()
    user_repo.exists.return_value = True
    service = UserService(user_repo)
    
    with pytest.raises(DuplicateUserError):
        service.register("existing@example.com", "password")

常见问题与解决方案[编辑 | 编辑源代码]

问题 解决方案
测试依赖外部服务 使用Mock或Stub替代
测试随机行为 固定随机种子或注入随机数生成器
测试耗时操作 使用虚拟时钟或时间模拟
测试多线程代码 使用同步原语或测试专用线程调度器

进阶主题[编辑 | 编辑源代码]

  • 参数化测试(同一测试多组输入)
  • 基于属性的测试(如Hypothesis框架)
  • 突变测试(验证测试有效性)
  • 测试驱动开发(TDD)

通过系统化的单元测试实践,开发者可以构建更健壮、可维护的代码库,显著降低生产环境中的缺陷率。