How to patch a module's internal functions with mock?

不想你离开。 提交于 2019-12-31 08:57:28

问题


By "internal function", I mean a function that is called from within the same module it is defined in.

I am using the mock library, specifically the patch decorators, in my unit tests. They're Django unit tests, but this should apply to any python tests.

I have one module with several functions, many of which call each other. For example (fictitious code, ignore the lack of 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

These are part of a deeper calling chain (func1 -> func2 -> build_cart -> add_tax), all of which are in the same module.

In my unit tests, I'd like to disable taxes to get consistent results. As I see it, my two options are 1) patch out TAX_LOCATION (with an empty string, say) so that add_tax doesn't actually do anything or 2) patch out add_tax to simply return (0, price).

However, when I try to patch either of these the patch seems to work externally (I can import the patched part inside the test and print it out, getting expected values), but seems to have no effect internally (the results I get from the code behave as if the patch were not applied).

My tests are like this (again, fictitious code):

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

Does anyone know if it's possible for mock to patch out functions used internally like this, or am I out of luck?


回答1:


The answer: Clean up your darned imports

@patch('mymodule.TAX_LOCATION', '') did indeed patch things appropriately, but since our imports at the time were very haphazard -- sometimes we imported mymodule.build_cart, sometimes we imported project.mymodule.build_cart -- instances of the "full" import were not patched at all. Mock couldn't be expected to know about the two separate import paths... without being told explicitly, anyway.

We've since standardized all our imports on the longer path, and things behave much more nicely now.




回答2:


another option is to explicitly call patch on the function:

mock.patch('function_name')

and to support both running directly or from py.test etc:

mock.patch(__name__ + '.' + 'function_name')



回答3:


I'd like to add solution other than accepted one. You can also patch the module before it's been imported in any other modules and remove patch at the end of your test case.

#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()



回答4:


I'm pretty sure your problem is that you are importing 'mymodule' inside your test functions, and therefore the patch decorator has no chance of actually patching. Do the import at the top of the module, like any other import.



来源:https://stackoverflow.com/questions/5341147/how-to-patch-a-modules-internal-functions-with-mock

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!