如何使用mock修补模块的内部函数?

ete*_*ode 23 python patch mocking

"内部函数"是指从定义的同一模块中调用的函数.

我在单元测试中使用模拟库,特别是补丁装饰器.它们是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
Run Code Online (Sandbox Code Playgroud)

这些是更深层调用链的一部分(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
Run Code Online (Sandbox Code Playgroud)

有谁知道模拟是否可以修补这样内部使用的功能,还是我运气不好?

ete*_*ode 15

答案是:清理你的进口产品

@patch('mymodule.TAX_LOCATION', '') 确实适当修补了一些东西,但由于我们当时的进口非常随意 - 有时我们进口mymodule.build_cart,有时我们进口project.mymodule.build_cart- "完全"进口的实例根本没有修补.无论如何,模拟不能被要求知道两个单独的导入路径......没有明确说明.

我们已经在较长的路径上标准化了所有的进口,现在事情表现得更好.


vim*_*vim 8

另一种选择是在函数上显式调用patch:

mock.patch('function_name')
Run Code Online (Sandbox Code Playgroud)

并支持直接运行或从py.test等:

mock.patch(__name__ + '.' + 'function_name')
Run Code Online (Sandbox Code Playgroud)

  • 这是我模拟在同一模块中定义的函数所需要的 (2认同)