“内部函数”是指从定义的同一模块中调用的函数。
我在单元测试中使用mock库,特别是patch装饰器。它们是Django单元测试,但这应该适用于任何python测试。
我有一个具有多个功能的模块,其中许多功能相互调用。例如(虚构代码,忽略缺少decimal.Decimal):
TAX_LOCATION = 'StateName, United States'
def add_tax(price, user):
tax = 0
if TAX_LOCATION == 'StateName, UnitedStates':
tax = price * .75
return (tax, price+tax)
def build_cart(...):
# build a cart object for `user`
tax, price = add_tax(cart.total, cart.user)
return cart
这些是更深层调用链的一部分(func1 - > func2 - > build_cart - > add_tax),所有这些都在同一个模块中。
在我的单元测试中,我想禁用税收以获得一致的结果。在我看来,我的两个选项是1)修补TAX_LOCATION(用空字符串,比如说),这样add_tax实际上什么都不做,或者2)修补add_tax只返回(0,价格)。
然而,当我尝试修补其中任何一个时,补丁似乎在外部工作(我可以在测试中导入修补部分并打印出来,获得预期值),但似乎内部没有效果(结果我从代码中获取的行为就像没有应用补丁一样。)
我的测试是这样的(再次,虚构的代码):
from mock import patch
from django.test import TestCase
class MyTests(TestCase):
@patch('mymodule.TAX_LOCATION', '')
def test_tax_location(self):
import mymodule
print mymodule.TAX_LOCATION # ''
mymodule.func1()
self.assertEqual(cart.total, original_price) # fails, tax applied
@patch('mymodule.add_tax', lambda p, u: (0, p))
def test_tax_location(self):
import mymodule
print mymodule.add_tax(50, None) # (0, 50)
mymodule.func1()
self.assertEqual(cart.total, original_price) # fails, tax applied
有谁知道模拟是否可以修补这样内部使用的功能,或者我运气不好?
答案 0 :(得分:14)
答案:清理您的darned进口
@patch('mymodule.TAX_LOCATION', '')
确实对事情进行了适当补丁,但由于当时我们的导入非常随意 - 有时我们导入mymodule.build_cart
,有时我们导入project.mymodule.build_cart
- “满”的实例“进口根本没有打补丁。无论如何,Mock不会被要求知道两条独立的导入路径......无论如何都没有明确说明。
我们已经在更长的路径上标准化了所有进口产品,现在情况表现得更好。
答案 1 :(得分:7)
另一种选择是在函数上显式调用patch:
mock.patch('function_name')
并支持直接运行或从py.test等运行:
mock.patch(__name__ + '.' + 'function_name')
答案 2 :(得分:3)
我想添加除已接受的解决方案之外的解决方案。您也可以在将模块导入任何其他模块之前对其进行修补,并在测试用例结束时删除补丁。
#import some modules that don't use module you are going to patch
import unittest
from mock import patch
import json
import logging
...
patcher = patch('some.module.path.function', lambda x: x)
patcher.start()
import some.module.path
class ViewGetTests(unittest.TestCase):
@classmethod
def tearDownClass(cls):
patcher.stop()
答案 3 :(得分:1)
我很确定你的问题是你在测试函数中导入'mymodule',因此补丁装饰器没有实际修补的机会。像其他任何导入一样,在模块顶部进行导入。