一般情况下,我希望禁用尽可能少的代码,并且我希望它是显式的:我不希望被测试的代码决定它是否是测试,我希望测试告诉代码“嘿,顺便说一下,我正在进行单元测试,请你不要打电话给solr,相反,请你能把你发送到solr的东西放在这个地方,这样我就能检查一下“。我有我的想法,但我不喜欢他们中的任何一个,我希望有一个很好的pythonic方式来做到这一点。
答案 0 :(得分:7)
您可以使用Mock objects拦截您不想执行的方法调用。
例如。您有一些类A
,您不希望在测试期间调用方法no()
。
class A:
def do(self):
print('do')
def no(self):
print('no')
模拟对象可以从A
继承并覆盖no()
无效。
class MockA(A):
def no(self):
pass
然后,您将在测试代码中创建MockA
个对象,而不是A
个。另一种进行模拟的方法是让A
和MockA
实现一个公共接口InterfaceA
。
有大量的模拟框架可供使用。请参阅StackOverflow: Python mocking frameworks。
答案 1 :(得分:5)
使用Michael Foord的Mock 在您的单元测试中执行此操作:
from mock import Mock
class Person(object):
def __init__(self, name):
super(Person, self).__init__()
self.name = name
def say(self, str):
print "%s says \"%s\"" % (self.name, str)
...
#In your unit test....
#create the class as normal
person = Person("Bob")
#now mock all of person's methods/attributes
person = Mock(spec=person)
#talkto is some function you are testing
talkTo(person)
#make sure the Person class's say method was called
self.assertTrue(person.say.called, "Person wasn't asked to talk")
#make sure the person said "Hello"
args = ("Hello")
keywargs = {}
self.assertEquals(person.say.call_args, (args, keywargs), "Person did not say hello")
答案 2 :(得分:1)
我遇到的一个大问题是依赖注入的机制。我现在已经想到了这一部分。
我需要在两个地方以完全相同的方式导入模块以成功注入新代码。例如,如果我有以下要禁用的代码:
from foo_service.foo import solr
solr.add(spam)
我似乎无法在我的测试运行中执行此操作:
from foo import solr
solr = mock_object
python解释器必须将模块foo_service.foo
和foo
视为不同的条目。我将from foo import solr
更改为更明确的from foo_service.foo import solr
,并成功注入了我的模拟对象。
答案 3 :(得分:0)
有两种方法可以做到这一点:没有,或者对DI进行最小化,修改源代码
cleanest方式正在使用dependency injection,但是I don't really like extensive monkeypatching,并且有些事情是不可能/难以做到的dependency injection变得容易。
答案 4 :(得分:0)
通常,当出现类似这样的事情时,您可以使用Monkey Patching(也称为Duck Punching)来获得所需的结果。查看this link以了解有关Monkey Patching的更多信息。
在这种情况下,例如,您将覆盖solr以仅打印您要查找的输出。
答案 5 :(得分:0)
我知道这是模拟对象的典型用例,但这也是一个古老的论点...... Mock对象是必需的还是 evil ?
我站在那些认为嘲笑邪恶的人身边,并试图避免改变测试代码。我甚至认为修改测试代码的这种需要是代码味道......
如果您希望更改或拦截内部函数调用以进行测试,您还可以在生成代码和测试代码提供的实例化时间内将此函数设置为显式外部依赖项集。如果你这样做,问题就会消失,你最终会得到一个更干净的界面。
请注意,这样做不需要在内部或正在执行的测试中更改测试代码。