修补在单元测试的依赖项中动态生成的数据类属性

Patch a dataclass attribute which is dynamically generated in its dependency in unit testing

我是单元测试的新手。我过去使用过模拟、修补,但我遇到的情况对我来说创建单元测试有点复杂。

所以我有一个文件:parent.py 包含以下数据 class

import multiprocessing
from dataclasses import dataclass

@dataclass
class ParentClass:
    cpu_count: int = multiprocessing.cpu_count() 

我有另一个模块 child.py 具有以下数据 class

from Whosebug.parent import ParentClass
from dataclasses import dataclass

@dataclass
class ChildClass(ParentClass):
      some_attribute_1: int = 1
      some_attribute_2: int = 2
      ....

最后,我有第三个模块 actual_function.py 使用这些数据 classes.

from stack_overflow.child import ChildClass


def get_cpu_count_and_attributes(cc: ChildClass):
    return cc.cpu_count, cc.some_attribute_1 

在这里,我想对print_cpu_count_and_attributes函数进行单元测试。修补在这里如何工作?我创建了以下测试用例,但它失败了。我系统中的 cpu_count 是 16,但我想用 return 值 8 模拟它,以便它可以在具有不同内核数的其他机器上运行

from unittest import mock
from stack_overflow.actual_function import *
from stack_overflow.child import ChildClass


@mock.patch('stack_overflow.parent.multiprocessing.cpu_count', return_value=8)
def test_print_cpu_count_and_attributes():
    cc = ChildClass()
    assert get_cpu_count_and_attributes(cc) == (8, 1)

这是文件夹结构。

Whosebug
├── __init__.py
  ├── actual_function.py
  ├── child.py
  ├── parent.py
  └── test_function.py

如果您正在尝试测试 ChildClass,您应该为它设置路径,而不是不同模块中的父项。

启发式模拟:

  1. 修补你测试的内容
  2. 尽可能接近目标函数的补丁

你的补丁不起作用的原因是 python 不会 re-evaluate 模块和 class 补丁后的层次结构。由于 python 是动态的,所以发生的事情是

  1. 家长class评价
  2. 子class参考父class对象
  3. 评估
  4. 您在父模块中修补父 class,但在 actual_function 中测试代码,并且 ChildClass 引用了旧的原始 Parent,因为 mock实际上是在 parent.py 命名空间中更改 Parent 的对象属性。

另外,看看mock documentation on patching point


您的案例示例:

with mock.patch.object(ChildClass, 'cpu_count', new_callable=mock.PropertyMock) as m:
    m.return_value = 42
    get_cpu_count_and_attributes(ChildClass())

你不应该改变 inherited attributes/properties,你应该把补丁 放在目标的顶部 (因此得名 ;) )