You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

pytest如何为含KeyboardInterrupt块的函数编写100%覆盖率用例

pytest覆盖KeyboardInterrupt异常分支实现100%覆盖率方案

问题说明

  • 被测函数loadingBar会捕获KeyboardInterrupt(用户按Ctrl+C触发的中断)执行进度保存逻辑,需要编写pytest用例覆盖该异常分支,满足coverage工具100%代码覆盖率要求
  • 原被测代码main_game.py存在几处运行时错误,先给出修正后的可运行版本:
# main_game.py
import json

class Cache:
    other_details = dict()

def save_progress_for_future(progress):
    file = open('progress.json', 'w')
    content = {'progress_percent':progress, **Cache.other_details}
    json.dump(content, file)
    file.close()

# 对齐except块的函数调用命名
save_progress_in_file = save_progress_for_future

def get_progress_percent():
    return 0

def loadingBar():
    progress = 0
    while True:
        try:
            ...  # 其他业务逻辑
            progress = get_progress_percent()
            print('\r Loading' + str('.' * progress//10) + '\r', end='')
        except KeyboardInterrupt:
            save_progress_in_file(progress)
            # 补充中断后的退出逻辑,避免死循环
            return

实现思路

不需要实际向测试进程发送系统中断信号,直接通过mock在try块逻辑中主动抛出KeyboardInterrupt异常,即可稳定模拟Ctrl+C场景,避免把测试进程本身杀了导致用例失败

  • 用mock替换get_progress_percent函数,指定调用时抛出KeyboardInterrupt,精准控制中断触发时机
  • mock掉进度保存函数,验证中断触发后保存逻辑是否被正确调用、入参是否符合预期
  • 确保异常触发后循环能正常退出,避免测试卡死

测试用例编写

新建测试文件test_main_game.py,代码如下:

# test_main_game.py
from unittest.mock import patch, MagicMock
import main_game

def test_keyboard_interrupt_triggers_save():
    # 模拟第一次获取进度就触发Ctrl+C中断
    mock_progress = MagicMock(side_effect=KeyboardInterrupt)
    mock_save_func = MagicMock()

    # patch替换被测模块中的依赖
    with patch("main_game.get_progress_percent", mock_progress), \
         patch("main_game.save_progress_in_file", mock_save_func):
        main_game.loadingBar()

    # 断言中断后确实调用了保存逻辑,初始进度值为0
    mock_save_func.assert_called_once_with(0)

def test_interrupt_at_specific_progress():
    target_progress = 70
    call_times = 0
    def progress_mock_logic():
        nonlocal call_times
        call_times += 1
        # 第4次获取进度时触发中断,此时进度为70
        if call_times == 4:
            raise KeyboardInterrupt
        return target_progress
    
    mock_save_func = MagicMock()
    with patch("main_game.get_progress_percent", side_effect=progress_mock_logic), \
         patch("main_game.save_progress_in_file", mock_save_func):
        main_game.loadingBar()
    
    # 断言保存的进度值和触发中断时的进度一致
    mock_save_func.assert_called_once_with(target_progress)

覆盖率验证步骤

  • 安装依赖:pip install pytest coverage pytest-mock
  • 执行测试并统计覆盖率:coverage run -m pytest test_main_game.py -v
  • 生成终端覆盖率报告:coverage report -m
  • 此时报告中main_game.py的所有代码行都会被标记为已覆盖,包括KeyboardInterrupt对应的except分支

注意事项

  • patch路径必须写对:要patch被测模块main_game下导入的依赖函数,不要patch错模块路径导致mock不生效
  • 原死循环逻辑必须在异常处理后加退出逻辑(return/break),否则测试会无限卡住
  • 不要用发送系统信号的方式模拟中断,信号会作用于整个测试进程,容易导致测试框架本身异常退出

内容的提问来源于stack exchange,提问作者Aman Ahmed Siddiqui

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 09:00:56