python 单元测试未覆盖 except 块内的代码

python unit test is not covering code inside the except block

根据我在 test_main_version_exception 单元测试中的理解,compare_version.version_match() 函数会引发异常,理想情况下,该异常应该被 except 块捕获。但由于某种原因,流程不会进入 except 块。 (第一个似乎工作正常)

有人可以帮我解决这个问题吗?也欢迎任何改进这些测试的建议。

main.py 文件

try:
  is_version_same = compare_version.version_match()
  if is_version_same:
    LOGGER.info("Upgrade not required")
    sys.exit()
except compare_version.VersionError as error: #custom error
    #### these two statements  are not getting covered when I run test_main_version_exception(mocker):
    LOGGER.exception("Exception occurred in compare_version: %s", error) 
    sys.exit(f"Exception occurred in compare_version: {error}")

单元测试:

def test_main_same_version(mocker):
    mocker.patch(
        "compare_version.version_match",
        return_value=True,
    )
    with pytest.raises(SystemExit) as ex:
        main.main()
    assert ex.type == SystemExit

def test_main_version_exception(mocker):
    mocker.patch(
        "compare_version.version_match",
        side_effect=VersionError,
    )
    with pytest.raises(VersionError) as ex:
        main.main()
    assert ex.type == VersionError

您必须通过 mocker.patch("main.compare_version.version_match", ...) 专门修补 main.py 中使用的 compare_version,无论它是否是:

  1. 导入到 main.py
    from some_file import compare_version
    
  2. 或在main.py中定义:
    class compare_version:  # Sample implementation only as you haven't included this in the question
        version_match = lambda: None
        VersionError = Exception
    

作为documented:

a.py
    -> Defines SomeClass

b.py
    -> from a import SomeClass
    -> some_function instantiates SomeClass

Now we want to test some_function but we want to mock out SomeClass using patch()... If we use patch() to mock out a.SomeClass then it will have no effect on our test; module b already has a reference to the real SomeClass and it looks like our patching had no effect.

The key is to patch out SomeClass where it is used (or where it is looked up). In this case some_function will actually look up SomeClass in module b, where we have imported it. The patching should look like:

@patch('b.SomeClass')

此外,您的断言 with pytest.raises(VersionError) as ex: 是不正确的,因为您的实现不再引发 VersionError,而是捕获该错误并引发 SystemExit。所以你需要在这里断言的是它是否引发 SystemExit。如果您真正需要的是 VersionError,则替换此行:

sys.exit(f"Exception occurred in compare_version: {error}")

收件人:

raise  # Will re-raise the previous exception, which is the VersionError

示例 运行 您可能想用作参考。

main.py

import sys

# # Whether it is defined in another file
# from some_file import compare_version
# # Or it is defined here
class compare_version:
    version_match = lambda: None
    VersionError = ValueError


def main():
    try:
      is_version_same = compare_version.version_match()
      if is_version_same:
        print("Upgrade not required")
        sys.exit()
    except compare_version.VersionError as error: #custom error
        print("Exception occurred in compare_version: %s", error) 

        # If you want this block to raise SystemExit
        sys.exit(f"Exception occurred in compare_version: {error}")

        # If you want this block to re-raise VersionError
        # raise

test_main.py

import pytest

import main


def test_main_same_version(mocker):
    mocker.patch(
        "main.compare_version.version_match",
        return_value=True,
    )
    with pytest.raises(SystemExit) as ex:
        main.main()
    assert ex.type == SystemExit


def test_main_version_exception(mocker):
    mocker.patch(
        "main.compare_version.version_match",
        side_effect=main.compare_version.VersionError,
    )
    with pytest.raises(SystemExit) as ex:
        main.main()
    assert ex.type == SystemExit

输出

$ pytest -q -rP
================================================================================================= PASSES ==================================================================================================
_________________________________________________________________________________________ test_main_same_version __________________________________________________________________________________________
------------------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------------------
Upgrade not required
_______________________________________________________________________________________ test_main_version_exception _______________________________________________________________________________________
------------------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------------------
Exception occurred in compare_version: %s 
2 passed in 0.04s