Python unittest mock: Is it possible to mock the value of a method's default arguments at test time?

谁都会走 提交于 2019-12-01 15:26:31
chepner

Functions store their parameter default values in the func_defaults attribute when the function is defined, so you can patch that. Something like

def test_build_url(self):
    """ If only endpoint is supplied should default to settings"""

    # Use `func_defaults` in Python2.x and `__defaults__` in Python3.x.
    with patch.object(build_url, 'func_defaults', ('domain',)):
      result = build_url('/end')
      expected = 'domain/end'

    self.assertEqual(result,expected)

I use patch.object as a context manager rather than a decorator to avoid the unnecessary patch object being passed as an argument to test_build_url.

I applied the other answer to this question, but after the context manager, the patched function was not the same as before.

My patched function looks like this:

def f(foo=True):
    pass

In my test, I did this:

with patch.object(f, 'func_defaults', (False,)):

When calling f after (not in) the context manager, the default was completely gone rather than going back to the previous value. Calling f without arguments gave the error TypeError: f() takes exactly 1 argument (0 given)

Instead, I just did this before my test:

f.func_defaults = (False,)

And this after my test:

f.func_defaults = (True,)

An alternate way to do this: Use functools.partial to provide the "default" args you want. This isn't technically the same thing as overriding them; the call-ee sees an explicit arg, but the call-er doesn't have to provide it. That's close enough most of the time, and it does the Right Thing after the context manager exits:

# mymodule.py
def myfunction(arg=17):
    return arg

# test_mymodule.py
from functools import partial
from mock import patch

import mymodule

class TestMyModule(TestCase):
    def test_myfunc(self):
        patched = partial(mymodule.myfunction, arg=23)
        with patch('mymodule.myfunction', patched):
            self.assertEqual(23, mymodule.myfunction())  # Passes; default overridden
        self.assertEqual(17, mymodule.myfunction()) # Also passes; original default restored

I use this for overriding default config file locations when testing. Credit where due, I got the idea from Danilo Bargen here

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