模拟类实例而不调用 `__init__` 并模拟它们各自的属性 [英] Mock class instances without calling `__init__` and mock their respective attributes

查看:81
本文介绍了模拟类实例而不调用 `__init__` 并模拟它们各自的属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有复杂 __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 的更改需要访问附加属性,我们现在必须同时更改实现其他内容(SuperClassMockMyClass,或者在本例中为 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆