跳转至

mock 模块

Python mock 模块详解

mock 模块(从 Python 3.3 开始集成为 unittest.mock)用于在测试中替换和模拟对象的行为。它可以帮助模拟复杂的依赖关系,使单元测试更加独立和可控。

1. 基本概念

mock 模块的核心类是 Mock,它允许你创建“伪造”的对象,这些对象可以被动态地赋予任何属性和行为。

from unittest.mock import Mock

# 创建一个 Mock 对象
mock_obj = Mock()

2. 模拟方法和属性

  • 模拟方法:

python mock_obj.some_method.return_value = 42 result = mock_obj.some_method() assert result == 42

  • 模拟属性:

python mock_obj.some_attr = 'mocked attribute' assert mock_obj.some_attr == 'mocked attribute'

3. 检查方法调用

  • 检查方法是否被调用:

python mock_obj.some_method() mock_obj.some_method.assert_called()

  • 检查方法调用次数:

python mock_obj.some_method() mock_obj.some_method() mock_obj.some_method.assert_called_once() # 检查是否只调用了一次

  • 检查方法调用的参数:

python mock_obj.some_method('hello', 'world') mock_obj.some_method.assert_called_with('hello', 'world')

  • 检查方法的调用顺序:

```python from unittest.mock import call

mock_obj.some_method('first') mock_obj.some_method('second')

mock_obj.some_method.assert_has_calls([call('first'), call('second')]) ```

4. 使用 patch 装饰器/上下文管理器

patchmock 模块中最常用的功能之一,它可以临时替换对象或模块的属性。

  • 使用 patch 装饰器:

```python from unittest.mock import patch

@patch('module.ClassName') def test_something(mock_class): instance = mock_class.return_value instance.method.return_value = 'mocked'

  result = instance.method()
  assert result == 'mocked'

```

  • 使用 patch 上下文管理器:

```python with patch('module.ClassName') as mock_class: instance = mock_class.return_value instance.method.return_value = 'mocked'

  result = instance.method()
  assert result == 'mocked'

```

5. 使用 patch.object 替换对象属性

patch.object 允许替换特定对象的属性或方法。

class MyClass:
    def method(self):
        return 'original'

my_obj = MyClass()

with patch.object(my_obj, 'method', return_value='mocked'):
    result = my_obj.method()
    assert result == 'mocked'

6. MagicMock

MagicMockMock 的一个子类,它模拟了所有魔法方法(如 __len____getitem__ 等)。

from unittest.mock import MagicMock

mock_list = MagicMock()

# 模拟 list 的行为
mock_list.__len__.return_value = 10
assert len(mock_list) == 10

mock_list.__getitem__.return_value = 'mocked'
assert mock_list[0] == 'mocked'

7. 自动补全属性和方法

mock 对象的任意属性和方法默认都是 Mock 对象。

mock_obj = Mock()

# 动态生成属性和方法
result = mock_obj.anything.you.can.imagine()
assert isinstance(result, Mock)

8. side_effect 用法

side_effect 可以指定调用方法时的副作用。它可以是一个函数、异常或一个返回值序列。

  • 抛出异常:

python mock_obj.method.side_effect = ValueError("Error occurred") try: mock_obj.method() except ValueError as e: assert str(e) == "Error occurred"

  • 根据输入参数返回不同值:

```python def side_effect(arg): return arg * 2

mock_obj.method.side_effect = side_effect assert mock_obj.method(3) == 6 ```

  • 返回不同的序列值:

python mock_obj.method.side_effect = [1, 2, 3] assert mock_obj.method() == 1 assert mock_obj.method() == 2 assert mock_obj.method() == 3

9. mock_open 读取文件

mock_open 用于模拟文件读取操作,特别是在测试需要模拟 open() 函数时。

from unittest.mock import mock_open, patch

m = mock_open(read_data='file content')

with patch('builtins.open', m):
    with open('file.txt') as f:
        result = f.read()
        assert result == 'file content'

10. 重置 Mock 对象

reset_mock() 方法用于重置 mock 对象的所有调用记录和状态。

mock_obj = Mock()
mock_obj.method()
mock_obj.reset_mock()
assert mock_obj.method.call_count == 0

总结

unittest.mock 模块为测试提供了强大的工具,使得你能够轻松地模拟对象、替换依赖并检查交互。它可以帮助你编写更健壮的单元测试,使测试代码与实际代码解耦。