Python 模拟:为单元测试引发第 3 方异常

Python Mock: raise 3rd party exception for unit testing

假设我有一个方法 is_validate,它在内部调用库 gateway.service

中的 validate 方法
import gateway.service
from gateway.service.exception import ValidatorException

def is_validate():
   try:
       gateway.service.validate() # which throws ValidatorException
       return True
   except ValidatorException ex:
       return False

如何对is_validate方法进行单元测试,模拟gateway.service.validate抛出ValidatorException

您可以通过以下组合来做到这一点:

  • 模拟一个函数(创建一个伪造的函数版本来指示它returns);
  • monkeypatch使用模拟版本实现实际功能;
  • 并使用 pytest 实际 运行 测试。

我已经写了一篇关于如何做到这一点的描述(摘自我自己的作品)here,以防我知道有用的例子。

但我认为您需要在代码中这样做:

定义一个 pytest fixture 来模拟你想要测试的场景,使用 monkeypatch 从 is_validate().

的部分伪造你想要的结果

以及检查是否引发 ValidatorException 的测试;在测试中引发错误的代码在 pytest 夹具中。那里定义的整个 pytest fixture 作为参数传递给测试。

import pytest
from unittest import TestCase

import gateway.service
from gateway.service.exception import ValidatorException


# Create object for accessing unittest assertions
assertions = TestCase("__init__")


@pytest.fixture
def validation_fails(monkeypatch):
    """
    Mocks a call to gateway.service.validate().
    """

    def mock_validate(*args, **kwargs):
        """Mock an absolute file path."""
        raise ValidatorException

    # Replace calls to existing methods with the mocked versions
    monkeypatch.setattr(gateway.service, "validate", mock_validate)


def test_validation_fails(validation_fails):
    """Test validation."""

    # check that the correct exception is raised
    with assertions.assertRaises(ValidatorException):
        is_validate()

注意:这不包括让 pytest 为您的项目工作所需的任何设置。

-------------------------------------
mymodule.py
-------------------------------------

import os
def remove(file_path):
    if os.path.exists(file_path):
        os.remove(file_path)
    else:
        print('File does not exist')

-------------------------------------

from mymodule import remove
import mock
import unittest
class RemoveTestCase(unittest.TestCase):
    
    @mock.patch('mymodule.os.path')
    @mock.patch('mymodule.os.remove')
    def test_remove(self, mock_os_remove, mock_os_path):
        mock_os_path.exists.return_value = True
#side_effect
        remove("any path")
        mock_os_remove.assert_called_with("any path")

我能够通过使用存在 is_validate 方法的模块名称引用它来模拟 gateway.service.validate

例如:@mock.patch('mymodule.gateway.service.validate')

参考此 doc 了解更多信息