模拟班级的建设 [英] Mocking a class's construction
问题描述
我刚刚开始使用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屋!