3

I'm using the mock library and unittest2 in order to test different aspects of my software project.

At the moment I have the following question: is it possible to mock a function so that the default keyword argument is different, but the functionality remains?

Say I have the following code

class C():
  def fun(self, bool_arg = True):
    if bool_arg:
      return True
    else
      return False

What if I want to mock C.fun:

C.fun = mock.Mock(???)

so that every instance of C will replace keyword 'bool_arg' with False, instead of True and the result of:

c = C()
c.fun()

returns:

False

mpaf
  • 6,597
  • 6
  • 38
  • 42

2 Answers2

3

you can also try to wrap your function. Something on the line of

def wrapper(func, bool_arg):
    def inner(*args, **kwargs):
        kwargs['bool_arg']=bool_arg
        return func(*args, **kwargs)
    return inner

and

class C():
    def fun(...):
        ...

c = C()
c.fun = wrapper(fun, False)

should work

Edit

If you want to change the default for the class and not for a particular instance you can create a derived class and redefine fun wrapping the method of C. Something on the line (I don't have now the time to test it):

class D(C):
    def fun(self, *args, **kwargs):
        f = wrapper(C.f, False)
        return f(*args, **kwargs)

Then about the suggestion of @Ber, you can define def wrapper(func, **wrapkwargs) and then instead of kwargs['bool_arg']=bool_arg do

for i in wrapkwargs.iteritems():  #wrapkwargs is a dictionary
    kwargs[i[0]] = i[1]
Francesco Montesano
  • 8,485
  • 2
  • 40
  • 64
  • 1
    Nice. Thid could be extended to wrap any function, supplying any list of keyword args. – Ber Jan 30 '13 at 14:31
  • Thanks for your answer! I was actually looking for replacing the Class default keyword argument to be replaced for every instance of it. But I adapted your solution a little so that instead of: c.fun = wrapper(...) I did: class C.fun = wrapper(...). And it worked for all instances! – mpaf Jan 31 '13 at 07:39
  • Ber, how would you generalize the above to accept any keyword argument that one wants to wrap ? – mpaf Jan 31 '13 at 07:48
  • @mpaf you mean that you have changed the implementation of class C substituting self.fun with a wrapped version? – Francesco Montesano Jan 31 '13 at 07:58
  • 1
    @mpaf: if you want to get a better understanding of wrapper (and consequently decorators) I suggest you to read http://stackoverflow.com/a/1594484/1860757 and http://simeonfranklin.com/blog/2012/jul/1/python-decorators-in-12-steps/ – Francesco Montesano Jan 31 '13 at 08:00
  • yes that's what I meant. But actually I now fail to get it working on my actual (more complex) class which has more keyword and non-keyword arguments. I get "TypeError: got multiple values for keyword argument". So yes I probably don't understand wrappers really well. I'll check your link, thanks! – mpaf Jan 31 '13 at 08:04
  • Thanks for that first link, it is really good! According to that link, when you decorate a class method you should add a **self** parameter to your `def inner(self, *args,**kwargs)` so I guess that was missing from your answer? I managed to solve my problem. I was getting multiple values for keyword argument because I was invoking the function somewhere without the keyword (just following the normal function parameter order). So it was getting passed to *args. When I then replaced the keyword in **kwargs it was actually defining a second value for the same parameter. – mpaf Jan 31 '13 at 08:44
  • Now that I see, this answer does not require mock library. Wasn't mock supposed to automate or make the decoration process easier? – mpaf Jan 31 '13 at 08:49
2

You can try to use this code:

>>> import mock
>>> 
>>> class C():
...   def fun(self, bool_arg = True):
...     if bool_arg:
...       print "True"
...     else:
...       print "False"
... 
>>> c = C()
>>> funCopy = c.fun
>>> c.fun = mock.Mock(side_effect=lambda bool_arg=False: funCopy(bool_arg=bool_arg))
>>> c.fun()
False

Hope this helps

jvallver
  • 2,230
  • 2
  • 11
  • 20
  • Did not know that you could do default variable assignment inside a `lambda`. – jdotjdot Jan 30 '13 at 14:14
  • @jdotjdot You are right, but I think if mpaf make this question is because he can call the function changing the default variable value. – jvallver Jan 30 '13 at 14:17
  • @jdotjdot in english please – Francesco Montesano Jan 30 '13 at 14:29
  • @jdotjdot OK! no problem, and sorry for my english ;) – jvallver Jan 30 '13 at 14:48
  • Thanks for this solution, I would have accepted it as it was the first one, but I couldn't make it work for the case that I was looking for (maybe I wasn't too explicit in my question) which was that every instance of class C gets that keyword argument replaced. – mpaf Jan 31 '13 at 07:38