在Python中,禁用单元测试中特定代码的好方法是什么?

11 投票
6 回答
2966 浏览
提问于 2025-04-15 19:38

总的来说,我希望尽量少禁用代码,并且要明确:我不想让被测试的代码自己决定它是不是在测试中,我希望测试能够告诉那段代码“嘿,顺便说一下,我正在运行单元测试,你能不能不要调用solr,而是把你本来要发送给solr的内容放在这里,这样我可以检查一下”。我有一些想法,但我不太喜欢它们,我希望能找到一种好的、符合Python风格的方法来实现这个。

6 个回答

1

我之前遇到的一个大问题是关于依赖注入的操作。我现在已经搞明白这个部分了。

我需要在两个地方以完全相同的方式导入模块,这样才能成功地注入新的代码。例如,如果我有以下代码想要禁用:

from foo_service.foo import solr
solr.add(spam)

我在我的测试运行器中似乎无法做到这一点:

from foo import solr
solr = mock_object

Python 解释器可能把模块 foo_service.foofoo 当作不同的东西来处理。我把 from foo import solr 改成了更明确的 from foo_service.foo import solr,结果我的模拟对象成功注入了。

8

你可以使用模拟对象来拦截那些你不想执行的方法调用。比如,你有一个类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对象。另一种模拟的方法是让AMockA实现一个共同的接口,比如InterfaceA

市面上有很多模拟框架可以使用。可以参考StackOverflow: Python 模拟框架

特别推荐查看:谷歌的 Python 模拟框架

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")

撰写回答