模拟类实例而不调用 `__init__` 并模拟它们各自的属性 [英] Mock class instances without calling `__init__` and mock their respective attributes
问题描述
我有一个带有复杂 __init__
函数的 MyClass
类.
I have a class MyClass
with a complex __init__
function.
这个类有一个我想测试的方法 my_method(self)
.
This class had a method my_method(self)
which I would like to test.
my_method
只需要类实例中的属性 my_attribute
.
my_method
only needs attribute my_attribute
from the class instance.
有没有一种方法可以在不调用 __init__
的情况下模拟类实例,而是通过设置每个类实例的属性?
Is there a way I can mock class instances without calling __init__
and by setting the attributes of each class instance instead?
我有什么:
# my_class.py
from utils import do_something
class MyClass(object):
def __init__(self, *args, **kwargs):
# complicated function which I would like to bypass when initiating a mocked instance class
pass
def my_method(self):
return do_something(self.my_attribute)
我尝试了什么
@mock.patch("my_class.MyClass")
def test_my_method(class_mock, attribute):
instance = class_mock.return_value
instance.my_attribute = attribute
example_instance = my_class.MyClass()
out_my_method = example_instance.my_method()
# then perform some assertions on `out_my_method`
然而,这仍然使用了 __init__
,我希望我们可以绕过或模拟.
however this still makes usage of __init__
which I hope we can by-pass or mock.
推荐答案
正如我在评论中提到的,一种无需创建实例即可测试单个方法的方法是:
As I mentioned in the comments, one way to test a single method without having to create an instance is:
MyClass.my_method(any_object_with_my_attribute)
与quamrana 的答案中的两个选项一样,这个问题是我们现在扩大了任何未来的变化仅仅因为测试.如果对 my_method
的更改需要访问附加属性,我们现在必须同时更改实现和其他内容(SuperClass
、MockMyClass
,或者在本例中为 any_object_with_my_attribute_and_another_one
).
The problem with this, as with both options in quamrana's answer, is that we have now expanded the scope of any future change just because of the tests. If a change to my_method
requires access to an additional attribute, we now have to change both the implementation and something else (the SuperClass
, the MockMyClass
, or in this case any_object_with_my_attribute_and_another_one
).
让我们举一个更具体的例子:
Let's have a more concrete example:
import json
class MyClass:
def __init__(self, filename):
with open(filename) as f:
data = json.load(f)
self.foo = data.foo
self.bar = data.bar
self.baz = data.baz
def my_method(self):
return self.foo ** 2
这里是任何需要 MyClass
实例的测试.由于 __init__
中的文件访问,这很痛苦.一个更可测试的实现会将如何访问数据的细节和有效实例的初始化分开:
Here any test that requires an instance of MyClass
. is painful because of the file access in __init__
. A more testable implementation would split apart the detail of how the data is accessed and the initialisation of a valid instance:
class MyClass:
def __init__(self, foo, bar, baz):
self.foo = foo
self.bar = bar
self.baz = baz
def my_method(self):
return self.foo ** 2
@classmethod
def from_json(cls, filename):
with open(filename) as f:
data = json.load(f)
return cls(data.foo, data.bar, data.baz)
您必须将 MyClass("path/to/file")
重构为 MyClass.from_json("path/to/file")
,但是无论您在哪里已经拥有数据(例如在您的测试中)您可以使用例如MyClass(1, 2, 3)
无需文件即可创建实例(您只需要在 from_json
本身的测试中考虑该文件).这使得实例实际需要变得更加清晰,并且允许引入其他方式来构造实例而不改变接口.
You have to refactor MyClass("path/to/file")
to MyClass.from_json("path/to/file")
, but wherever you already have the data (e.g. in your tests) you can use e.g. MyClass(1, 2, 3)
to create the instance without requiring a file (you only need to consider the file in the tests of from_json
itself). This makes it clearer what the instance actually needs, and allows the introduction of other ways to construct an instance without changing the interface.
这篇关于模拟类实例而不调用 `__init__` 并模拟它们各自的属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!