繁体   English   中英

代码覆盖率:如何在 pytest 中为具有除 KeyboardInterrupt 块之外的函数编写 100% 覆盖率测试?

[英]Code Coverage: How to write 100% coverage tests in pytest for functions having except KeyboardInterrupt blocks?

我有一个捕获 KeyboardInterrupts 以获得附加功能的函数,我必须在 pytest 中编写测试,但我不知道如何创建一个测试用例来覆盖 KeyboardInterrupt 捕获块。

代码与此类似:

# main_game.py
class Cache:
    other_details = dict()

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

def loadingBar():
    progress = 0
    while True:
        try:
            ...  # other stuff
            progress = get_progress_percent()
            print('\r Loading' + str('.' * progress//10) + '\r', end='')
        except KeyboardInterrupt:
            save_progress_in_file(progress)

我将如何在其他测试文件(例如 test_main_game.py)中的 pytest 中编写测试以覆盖 KeyboardInterrupt 部分,以便覆盖 cli 工具在其报告中显示 100% 的代码覆盖率?

键盘中断信号、内存不足故障等异常通常是不确定的,因此无法保证它们是否以及何时会在正常执行流程中引发。 它们起源于操作系统级别,而不是解释器本身(例如与 ValueError 不同)。 鉴于此,没有可靠的方法来模拟这些条件的出现并与单元测试代码的执行正确对齐。

现在您可以做的是在try块中的某处模拟中断以引发异常以将执行重定向到except块内的代码。 为了做到这一点, #other stuff section 或get_progress_percent()函数中的某些代码应该在单元测试上下文中以某种方式引发 KeyboardInterrupt。

由于不知道 #other stuff中发生了什么,我将坚持使用get_progress_percent()

为此,需要对loadingBar()进行重构,以使其接受get_progress_percent()函数的委托,如下所示:

def loadingBar(progress_loader = get_progress_percent):
  progress = 0
  while (True):
    try:
      # other stuff
      progress = progress_loader()
      # print to stdout, etc...
    except KeyboardInterrupt:
      save_progress_in_file(progress)

现在,如果在没有参数的情况下调用loadingBar() ,它将假定progress_loader变量的默认值是您的默认get_progress_percent()函数。 这是您在程序中进行的实际调用。

要测试 except 块内的替代流程,您可以考虑使用对loadingBar()的重载调用创建额外的单元测试,并将引发 KeyboardInterrupt 的函数传递给它。

您的单元测试用例可能如下所示:

import unittest

class LoadingBarTestCase(unittest.TestCase):

  def testLoadingBar(self):
    """Test the code in try block"""

    loadingBar()

    # assertions for other stuff
    # also assert that save_progress_in_file() doesn't get called
    
  def testLoadingBarInterrupted(self):
    """Test the code in except block"""

    # mock function to raise the interrupt
    def interrupted_progress_loader():
        raise KeyboardInterrupt()

    # call loadingBar passing it a mock delegate
    loadingBar(interrupted_progress_loader)

    # assert that save_progress_in_file() got called by exception handler

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

因此,总结一下,一些特殊的边缘情况要求您的代码需要以某种方式进行调整,以使其对单元测试更加友好,这可能根本不是一件坏事。

我希望这会有所帮助😃

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM