如何使用coverage检测代码执行是否覆盖额外代码行
实现方法
你的思路完全可行,直接用coverage库自带的Python API就能实现全部逻辑,不需要手动解析.coverage二进制文件。
步骤1:生成基准覆盖率数据
先运行现有单元测试,拿到当前测试集已经覆盖的代码基线:
python -m coverage run -m pytest tests -vv
命令执行完成后会在当前目录生成.coverage文件,存储现有测试的覆盖信息。
步骤2:运行PDF遍历筛选脚本
下面的脚本可以直接实现你伪代码描述的逻辑,自动筛选出能提升覆盖率的PDF文件:
import os from coverage import Coverage from coverage.files import PathAliases from PyPDF2 import PdfReader # 替换成你存放PDF样本的目录路径 PDF_SAMPLE_DIR = "./path/to/your/pdf_files" # 只统计PyPDF2本身的代码覆盖率,排除测试脚本、第三方依赖的干扰 COVERAGE_TARGET = ["PyPDF2"] def get_text(path): reader = PdfReader(path) for page in reader.pages: text = page.extract_text() if __name__ == "__main__": # 加载基准覆盖率数据 base_cov = Coverage(source=COVERAGE_TARGET) base_cov.load() base_data = base_cov.get_data() path_alias = PathAliases() base_missing_lines = set() base_partial_lines = set() # 收集基准状态下未覆盖、部分覆盖的代码行 for src_file in base_data.measured_files(): norm_path = path_alias.canonical_filename(src_file) _, missing_lines, _ = base_cov.analysis(norm_path) base_missing_lines.update((norm_path, line_no) for line_no in missing_lines) # 收集分支覆盖不全的行(比如判断逻辑只走了True分支的情况) partial_arcs = base_data.partial_arcs(norm_path) for arc in partial_arcs: base_partial_lines.add((norm_path, arc[0])) base_partial_lines.add((norm_path, arc[1])) valuable_pdfs = [] # 遍历目录下所有PDF文件 for root, _, filenames in os.walk(PDF_SAMPLE_DIR): for filename in filenames: if not filename.lower().endswith(".pdf"): continue pdf_abs_path = os.path.abspath(os.path.join(root, filename)) # 初始化临时覆盖率采集器,结果只存在内存里,不写入磁盘 tmp_cov = Coverage(source=COVERAGE_TARGET, data_file=None) tmp_cov.start() try: get_text(pdf_abs_path) except Exception: # 触发异常的PDF往往能走到异常处理分支,同样有测试价值,不需要跳过 pass tmp_cov.stop() # 提取当前PDF运行时覆盖到的代码行 tmp_data = tmp_cov.get_data() current_covered = set() for src_file in tmp_data.measured_files(): norm_path = path_alias.canonical_filename(src_file) covered_lines = tmp_data.lines(norm_path) if covered_lines: current_covered.update((norm_path, line_no) for line_no in covered_lines) # 计算当前PDF带来的新增覆盖:命中原未覆盖行,或者命中原部分覆盖的行 new_full_cover = base_missing_lines.intersection(current_covered) new_partial_hit = base_partial_lines.intersection(current_covered) total_new = new_full_cover.union(new_partial_hit) if total_new: valuable_pdfs.append({ "path": pdf_abs_path, "new_line_count": len(total_new), "new_lines": total_new }) # 清空临时覆盖率数据,避免不同PDF的结果互相污染 tmp_cov.erase() # 按新增覆盖的行数倒序排列,优先展示覆盖率贡献最高的文件 valuable_pdfs.sort(key=lambda x: x["new_line_count"], reverse=True) for pdf in valuable_pdfs: print(f"有效样本: {pdf['path']},新增覆盖行数: {pdf['new_line_count']}")
实用优化建议
- 脚本默认捕获所有PDF解析异常,遇到损坏、格式不规范的PDF不会中断遍历,这类文件通常能触发异常处理分支,本身就是很有价值的测试样本。
- 覆盖率统计范围限定为PyPDF2源码,不会把遍历脚本、系统依赖的代码计入结果,避免统计误差。
- 如果样本量特别大,可以加个终止判断:当所有原未覆盖的代码行都被命中过,直接停止遍历,不需要跑完所有文件,能节省大量时间。
- 筛选出来的PDF可以按新增覆盖的行去重,避免加入内容重复、覆盖分支完全一致的样本,控制测试集体积。
内容的提问来源于stack exchange,提问作者Martin Thoma
相关产品推荐
相关产品推荐

