模拟班级的建设 [英] Mocking a class's construction

查看:97
本文介绍了模拟班级的建设的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚刚开始使用Python的 mock 库帮助编写更简洁和孤立的单元测试.我的情况是,我有一个类可以从多毛的格式中读取数据,并且我想在该类上测试一个以干净的方式呈现数据的方法 格式.

I'm just getting started with using Python's mock library to help write more concise and isolated unit tests. My situation is that I've got a class that reads in data from a pretty hairy format, and I want to test a method on this class which presents the data in a clean format.

class holds_data(object):
    def __init__(self, path):
        """Pulls complicated data from a file, given by 'path'.

        Stores it in a dictionary. 
        """
        self.data = {}
        with open(path) as f:
            self.data.update(_parse(f))

    def _parse(self, file):
        # Some hairy parsing code here
        pass

    def x_coords(self):
        """The x coordinates from one part of the data
        """
        return [point[0] for point in self.data['points']]

上面的代码是我所拥有的简化.实际上,_parse是一个相当重要的方法,我在功能级别上对此进行了测试.

The code above is a simplification of what I have. In reality _parse is a fairly significant method which I have test coverage for at the functional level.

但是,我希望能够在单元测试级别上测试x_coords.如果我通过给它一个路径来实例化此类,它将违反规则单元测试,因为:

I'd like to be able, however, to test x_coords at a unit test level. If I were to instantiate this class by giving it a path, it would violate the rules of unit tests because:

在以下情况下,测试不是单元测试:

A test is not a unit test if:

  • 它触及文件系统

因此,我希望能够为holds_data修补__init__方法,然后仅填写x_coords所需的self.data部分.像这样:

So, I'd like to be able to patch the __init__ method for holds_data and then just fill in the part of self.data needed by x_coords. Something like:

from mock import patch
with patch('__main__.holds_data.__init__') as init_mock:
    init_mock.return_value = None
    instance = holds_data()
    instance.data = {'points':[(1,1),(2,2),(3,4)]}
    assert(instance.x_coords == [1,2,3])

上面的代码可以正常工作,但是感觉它正在以一种相当round回的方式进行此测试.是否有更惯用的方法来修补构造函数,或者这是进行此操作的正确方法?另外,在我的课堂上还是在测试中我是否缺少代码气味?

The code above works but it feels like it's going about this test in a rather roundabout way. Is there a more idiomatic way to patch out a constructor or is this the correct way to go about doing it? Also, is there some code smell, either in my class or test that I'm missing?

要清楚地说明,我的问题是,在初始化期间,我的班级进行了大量数据处理,以组织将由x_coords之类的方法呈现的数据.我想知道修补所有这些步骤的最简单方法是什么,而不必提供输入的完整示例.我只想在控制数据使用的情况下测试x_coords的行为.

To be clear, my problem is that during initialization, my class does significant amounts of data processing to organize the data that will be presented by a method like x_coords. I want to know what is the easiest way to patch all of those steps out, without having to provide a complete example of the input. I want to only test the behavior of x_coords in a situation where I control the data it uses.

我关于这里是否有代码异味的问题归结为以下问题:

My question of whether or not there is code smell here boils down to this issue:

我敢肯定,如果我重构为将x_coords用作接受holds_data作为参数的独立函数,会更容易.如果更容易进行测试==更好的设计"成立,这将是可行的方法.但是,这需要x_coords函数来了解我通常会习惯的holds_data内部的更多信息.我应该在哪里进行权衡?更干净的代码或更干净的测试?

I'm sure this would be easier if I refactor to have x_coords be a stand alone function that takes holds_data as a parameter. If "easier to tests == better design" holds, this would be the way to go. However, it would require the x_coords function to know more about the internals of holds_data that I would normally be comfortable with. Where should I make the trade off? Cleaner code or cleaner tests?

推荐答案

由于您只对测试一种方法感兴趣,所以为什么不模拟整个HoldsData类并将其固定在x_coords方法上呢? ?

Since you're only interested in testing one method, why don't you just mock the entire HoldsData class and pin on it the x_coords method?

>>> mock = MagicMock(data={'points': [(0,1), (2,3), (4,5)]})
>>> mock.x_coords = HoldsData.__dict__['x_coords']
>>> mock.x_coords(mock)
[0, 2, 4]

这样,您将完全控制x_coords输入和输出(无论是副作用还是返回值).

This way you'll have full control over the x_coords input and output (either by side effect or return value).

注意:在py3k中,您可以执行mock.x_coords = HoldsData.x_coords,因为不再有unbound methods .

Note: In py3k you could just do mock.x_coords = HoldsData.x_coords since there are no more unbound methods.

这也可以在模拟对象的构造函数中完成:

That could also be done in the constructor of the mock object:

MagicMock(data={'points': [(0,1), (2,3), (4,5)]}, x_coords=HoldsData.__dict__['x_coords'])

这篇关于模拟班级的建设的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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