跳转到内容

Python 代码覆盖率

来自代码酷

Python代码覆盖率[编辑 | 编辑源代码]

代码覆盖率(Code Coverage)是衡量测试用例对源代码覆盖程度的指标,用于评估测试的完整性。在Python中,它帮助开发者识别未经测试的代码区域,从而提升软件质量。本章将详细介绍其原理、工具及实践方法。

核心概念[编辑 | 编辑源代码]

代码覆盖率通过分析测试执行期间触及的代码行、分支、函数等元素,计算以下常见指标:

  • 语句覆盖率:测试执行的代码行占总行数的比例
  • 分支覆盖率:测试覆盖的控制流分支(如if/else)比例
  • 函数覆盖率:被调用的函数占全部函数的比例
  • 条件覆盖率:布尔子表达式被评估为True/False的情况

数学表达式: 覆盖率=已执行代码数总代码数×100%

工具与实现[编辑 | 编辑源代码]

Python常用覆盖率工具:

  • coverage.py(主流选择)
  • pytest-cov(与pytest集成)

基础示例[编辑 | 编辑源代码]

安装工具:

pip install coverage pytest-cov

示例被测代码(calculator.py):

def add(a, b):
    return a + b

def subtract(a, b):
    return a - b

def multiply(a, b):
    if a == 0 or b == 0:
        return 0
    return a * b

测试文件(test_calculator.py):

import pytest
from calculator import *

def test_add():
    assert add(2, 3) == 5

def test_multiply():
    assert multiply(3, 4) == 12
    # 未测试a或b为0的情况

执行覆盖率分析:

coverage run -m pytest test_calculator.py
coverage report -m

输出结果示例:

Name             Stmts   Miss  Cover   Missing
---------------------------------------------
calculator.py       7      2    71%    6, 9
test_calculator.py  4      0   100%
---------------------------------------------
TOTAL              11      2    82%

进阶应用[编辑 | 编辑源代码]

分支覆盖率分析[编辑 | 编辑源代码]

使用--branch参数检测条件分支:

coverage run --branch -m pytest test_calculator.py

此时会发现multiply()a == 0 or b == 0的分支未被完全覆盖。

可视化报告[编辑 | 编辑源代码]

生成HTML报告更直观:

coverage html

示例报告结构:

pie title 覆盖率分布 "已覆盖" : 82 "未覆盖" : 18

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

场景:Web应用登录验证

def validate_login(username, password):
    if not username or not password:
        return False
    elif len(password) < 8:
        return False
    else:
        return db.check_credentials(username, password)

完整测试应覆盖: 1. 空用户名/密码 2. 密码长度不足 3. 有效凭证 4. 无效凭证

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

  • 目标覆盖率达到80%以上(关键模块建议95%+)
  • 优先保证复杂逻辑和核心功能的覆盖率
  • 结合持续集成(CI)自动化覆盖率检查
  • 注意覆盖率的局限性:高覆盖率≠无缺陷

常见问题[编辑 | 编辑源代码]

Q: 100%覆盖率是否意味着没有bug? A: 不是。覆盖率仅反映代码被执行,不验证逻辑正确性。例如:

def divide(a, b):
    return a / b  # 测试可能覆盖但未处理b=0异常

Q: 如何忽略无需测试的代码? 使用# pragma: no cover注释:

def deprecated_function():  # pragma: no cover
    pass