I am trying to mock two methods within a single function call. Below I have the method entry_point which then calls foo, which in-turn calls bar.
I want to ensure that both foo and bar are called, and I also want to inspect their arguments to see what the types and values are of the passed arguments.
foo.py
class MyClass:
def entry_point(self):
self.foo(x=2)
def foo(self, x):
self.bar(y=x*3)
def bar(self, y):
return y**2
test_foo.py
import unittest
from unittest.mock import patch
from foo import MyClass
class TestMyClass(unittest.TestCase):
def test_nested_patch(self):
mc = MyClass()
# I would like to find out what arguments are passed
# in to both foo() and bar()
with patch.object(mc, "foo") as mock1:
with patch.object(mc, "bar") as mock2:
mc.entry_point()
# PASSES
mock1.assert_called_once()
# FAILS HERE: "bar" not called
mock2.assert_called_once()
# ADDITONAL: I want to see the function arguments
_, kwargs1 = mock1.call_args
_, kwargs2 = mock2.call_args
self.assertEqual(kwargs1["x"], 2)
self.assertEqual(kwargs2["y"], 6)
AssertionError: Expected 'bar' to have been called once. Called 0 times.
I have tried it a few different ways, but the above code seemed to be the cleanest way to explain my situation.
I recognize I could get around this by calling mc.entry_point() within two different (non-nested) patch context managers (one for foo, one for bar) but that is not as clean and doesn't really give me full control over the function calls.
Related
Imagine a class like so:
class Foo():
def method_1(self):
bar = Bar()
bazz = Bazz(bar)
return bazz.method_2()
For unit testing, how can we mock the Bar object when we never call any methods on it, we're just passing it as a parameter to the Bazz constructor? (Yes, this is not ideal, but this pattern can be found in a top-level class wiring together different objects via dependency injection).
You do call the Bar object, when you execute: bar = Bar(), so you can easily mock it:
mock_bar = mocker.MagicMock(name='Bar')
mocker.patch('foo.Bar', new=mock_bar)
# mock_foo.return_value = the new mocked object
Currently dealing with some legacy python code. I need to test one of the functions, function foo . Now to do that I need to mock the return value given by another function that's called by foo which is function bar.
However both of these are outside of my testing package and when I try to mock out bar, foo is unaffected. Is there a way of doing this without moving everything into a class? (which is preferred, but it's legacy code).
A simple example:
foo_and_bar_package/foo_and_bar_module.py
def bar():
return 5
def foo():
return 2 * bar()
test_foo_and_bar_module.py
from unittest import TestCase
from unittest.mock import patch
from foo_and_bar_package.foo_and_bar_module import foo
class TestFoo(TestCase):
#patch('foo_and_bar_package.foo_and_bar_module.bar')
def test_foo(self, mock_bar):
mock_bar.return_value = 2
self.assertEqual(foo.foo(), 2 * mock_bar.return_value)
Is it possible to make a module callable with parameters?
I am trying to make a callable module on Python, following the question and its answers Callable modules, like so:
foo.py
import sys
class foo(object):
def __call__(self):
return 'callable'
sys.modules[__name__] = foo()
then I call it:
import foo
print(foo()) # 'callable'
but my objective is to pass a parameter in the callable module:
print(foo('parameter'))
Any ideas how can I accomplish that?
There's nothing special about the class you created (it's not even a ModuleType subclass), so there's nothing special about its __call__ method. If you want to call it with arguments, just add parameters to the __call__ definition:
import sys
class foo(object):
def __call__(self, x):
return f'callable, and called with {x}'
sys.modules[__name__] = foo()
And now, you can pass it an argument, exactly like any other callable object:
import foo
print(foo('hello'))
And the output is:
callable, and called with hello
From the comments, you tried to do this:
def __call__(a, self):
return a
But, like all methods in Python, __call__ wants self to come first. It doesn't care about the names (unless you call it with keyword arguments), just the order: the first parameter gets the receiver (the foo in foo('hello')), even if it you called that parameter a, and the second parameter gets the first normal argument (the 'hello'), even if you called that parameter self.
So, you're passing the module foo as the first parameter, a, and you return a, so it returns foo.
Which is why you got this:
<sta.foo object at 0x10faee6a0>
That isn't an error, that's the perfectly valid output that you get when you print out an instance of a class that doesn't define __repr__ or __str__.
I get an error when trying to bind a class method to a function. Why?
def foo():
print "Hello world"
class something(object):
bar = foo
test = something()
test.bar()
TypeError: foo() takes no arguments (1 given)
Also, if I am unable to modify foo, can I do this adaptation from within the class definition?
A simple way to do it is to wrap the function in a staticmethod inside A:
class A():
bar = staticmethod(foo)
>>> test = A()
>>> test.bar()
Hello world
A class method in python always takes at least one argument, usually called self. This example is taken from the official Python tutorial:
# Function defined outside the class
def f1(self, x, y):
return min(x, x+y)
class C:
f = f1
def g(self):
return 'hello world'
h = g
Note that both methods, regardless of whether they are defined outside or inside of the class, take self as an argument.
Edit: If you really can't change your foo function, then you can do something like this:
>>> def foo():
... print "Hello World"
...
>>> class something(object):
... def bar(self): foo()
...
>>> test = something()
>>> test.bar()
Hello World
When you call a class method this way you pass the class instance as the first parameter.
When you call test.bar what in fact happens is more like bar(test). You pass an argument to the method.
Class methods all have a first argument of the instance of the class. So add a parameter to your function and it would work.
The initial def creates a function object named foo. Since it's outside any class, it's just a function that takes no arguments. The assignment bar = foo just gives the new name test.bar to that same function object. The call test.bar(), however, assumes that bar is a class method, and passes the object test as the first argument to the method (the one that you would normally call self). You could call it as a static method with something.bar() and not get the error.
Remember that when a python class calls one of it's methods it will pass in a self argument. You need to account for that in your code:
def foo(self):
print ("Hello world")
class something(object):
bar = foo
test = something()
test.bar()
You can read all about classes in the Python Documentation
The easiest workaround to not passing in a self that I can think of is:
def foo():
print ("Hello world")
class something(object):
bar = [foo] # contains pointer to static method
test = something()
test.bar[0]() # calls the static method
Example code:
# -*- coding: utf-8 -*-
from functools import wraps
class MyClass(object):
def __init__(self):
pass
#decorator inside class
def call(f):
#wraps(f)
def wrapper(*args):
print 'Wrapper: ', args
return wrapper
#decorated 'method' without self
#call
def myfunc(a):
pass
c = MyClass()
c.myfunc(1)
Returns:
Wrapper: (<test3.MyClass object at 0xb788a34c>, 1)
Is this normal? Can someone explain?
If this is a feature I would use it in my library.
This is perfectly normal.
The function myfunc is replacecd by an instance of wrapper. The signature of wrapper is (*args). because it is a bound method, the first argument is the instance of MyClass which is printed out after the string `Wrapper: '.
What's confusing you?
It's worth noting that if you use call as a decorator from outside of MyClass, it will generate a TypeError. One way around this is to apply the staticmethod decorator to it but then you can't call it during class construction.
It's a little bit hacky but I address how to have it both ways here.
update after comment
it gets the instance as the first argument regardless of if you type self in the parameter list because after the class is created, and an instance instantiated, it is a bound method. when you call it in the form
#instance.call
def foo(bar):
return bar + 1
it expands to
def foo(bar):
return bar + 1
foo = instance.call(f)
but note that you are calling it on an instance! This will automatically expand to a call of the form
def foo(bar):
return bar + 1
foo = MyClass.call(instance, f)
This is how methods work. But you only defined call to take one argument so this raises a TypeError.
As for calling it during class construction, it works fine. but the function that it returns gets passed an instance of MyClass when it is called for the same reason that I explained above. Specifically, whatever arguments you explicity pass to it come after the implicit and automatic placement of the instance that it is called upon at the front of the argument list.
#call
def myfunc(a):
...
is equivalent to
def myfunc(a):
...
myfunc=call(myfunc)
The orginial myfunc may have expected only one argument, a, but after being decorated with call, the new myfunc can take any number of positional arguments, and they will all be put in args.
Notice also that
def call(f)
never calls f. So the fact that
def myfunc(a)
lacks the normal self argument is not an issue. It just never comes up.
When you call c.myfunc(1), wrapper(*args) gets called.
What is args? Well, since c.myfunc is a method call, c is sent as the first argument, followed by any subsequent arguments. In this case, the subsequent argument is 1. Both arguments are sent to wrapper, so args is the 2-tuple (c,1).
Thus, you get
Wrapper: (<test3.MyClass object at 0xb788a34c>, 1)