我应该如何在Python中使用mock进行测试?
我可以看到两种不同的方法将模拟注入到我想要测试的python代码中:我应该如何在Python中使用mock进行测试?,python,unit-testing,mocking,tdd,python-mock,Python,Unit Testing,Mocking,Tdd,Python Mock,我可以看到两种不同的方法将模拟注入到我想要测试的python代码中: 依赖项注入: 允许将协作类传递到被测试对象的构造函数中,并传入模拟对象(以及必要时的工厂,如Java) 猴子修补: 用一个模拟对象工厂(mock object factory)剔除测试模块中的协作类(这样构造协作类实际上会创建一个模拟对象)。我不需要允许它们通过构造函数注入或创建任何工厂 python模拟库(例如,或)似乎支持这两种方法。我应该在Python中使用哪种方法,是明智的还是有更好的方法 尽可能多地使用DI通常是有用
python模拟库(例如,或)似乎支持这两种方法。我应该在Python中使用哪种方法,是明智的还是有更好的方法 尽可能多地使用DI通常是有用的,但有时它就是不可行, 因为你:
- 使用内置函数或对象(如文件)
- 第三方职能
- 使用非确定性对象/调用等
- 如果特定的依赖关系是针对单元的,则在产品和测试中的monkey patch中对其进行硬编码。
- 异常:如果在测试中使用生产依赖项可能对系统有害,请将其作为位置参数注入
- 例外情况:如果存在将monkey补丁应用于另一个单元依赖项的风险,并且这是不可取的,则将其作为位置参数注入
- 如果相同类型的依赖项对单元至关重要,则将其作为必需的位置参数注入
- 如果依赖项对单元不是必需的,则将其作为可选的关键字参数注入
术语 依赖注入:在Python上下文中,这个术语通常是专门指 Monkey Patching:(在测试代码中)在运行时与模块中绑定的对象不同。在实践中,这通常意味着使用 例子 假设我们有一个在测试过程中不受欢迎的带有副作用的函数,无论是破坏性的(向生产数据库写入无意义的内容)还是恼人的(例如,速度慢)。下面是后一种情况及其测试的示例:
def foo():
...
time.sleep(5)
...
return "bar"
...
def foo_test():
assertEqual(foo(), "bar")
我们的测试正常,但至少需要5秒钟。我们可以通过替换时间来避免等待。这两种策略是本问题的主题:
依赖注入
猴子修补术
为什么依赖注入?
我发现猴子补丁的优点和缺点更简单,所以我将分析重点放在依赖注入上
混乱的接口只是为了可测试性?
依赖项注入非常明确,但需要修改产品代码。猴子补丁并不明确,但不需要修改产品代码
程序员的本能反应是在修改产品代码进行测试之前做出许多牺牲。在考虑了注入所有依赖项后应用程序的外观之后,对monkey补丁的偏好似乎是不言而喻的。作为:
[E] ven内部API仍然需要开发人员和我
我也不喜欢和他们鬼混
我的论点是依赖注入只是为了可测试性
Python从来都不需要,也很少比其他语言更可取
技术。依赖注入在很多情况下都很重要
不过,作为一种结构/架构,它本身很有用
当编写单元测试时,这个话题自然而然地就出现了,但对那些提倡依赖注入的人的一种慈善解释认为,可测试性不是他们的主要动机。这种猴子补丁“让代码有点难以理解,我更愿意在某个地方看到:我们现在正在测试,所以这就是你获得时间的方式。”他详细阐述(3:14):
当我坐下来看我的代码时,我会想,‘我怎样才能更好地测试它?’我会改变产品代码,使其更易于测试,实际上这是更好的产品代码。我认为这是因为,如果你必须写一些能做一件事的东西,它能把一件事做好,但是如果你写一些能把两件事做好的东西,那是一件更好的事情。做好测试,除了成为一个好的产品,还能使代码变得更好。通过有两种用途,您必须真正考虑API,并且必须真正考虑这一段代码在做什么。通过让它很好地完成这两项工作,你得到了一个更好的、模块化的,
def foo(wait=time.sleep):
...
wait(5)
...
return "bar"
...
def foo_test():
assertEqual(foo(wait=mock.Mock()), "bar")
def foo():
...
time.sleep(5)
...
return "bar"
...
@mock.patch('time.sleep')
def foo_test():
assertEqual(foo(), "bar")
-- bar = foo()
++ bar = foo(wait=mywait)
-- def foo(wait=time.sleep):
++ def foo():
class OldClass(object):
...
def EnLolCat(self, misspelt_phrases):
lolcats = []
for album in self.albums:
for photo in album.Photos():
exif = photo.EXIF()
for text in misspelt_phrases:
geoText = text.Geo(exif)
if photo.canCat(geoText)
lolcat = photo.Cat(geoText)
self.lolcats = lolcats
def Lol(albums, misspelt_phrases):
lolcats = []
for album in albums:
for photo in album.Photos():
exif = photo.EXIF()
for text in misspelt_phrases:
geoText = text.Geo(exif)
if photo.canCat(geoText)
lolcat = photo.Cat(geoText)
return lolcats
class NewClass(object):
...
def EnLolCat(self, misspelt_phrases):
self.lolcats = Lol(
self.albums, misspelt_phrases)