How to mock asyncio coroutines? - python

The following code fails with TypeError: 'Mock' object is not iterable in ImBeingTested.i_call_other_coroutines because I've replaced ImGoingToBeMocked by a Mock object.
How can I mock coroutines?
class ImGoingToBeMocked:
#asyncio.coroutine
def yeah_im_not_going_to_run(self):
yield from asyncio.sleep(1)
return "sup"
class ImBeingTested:
def __init__(self, hidude):
self.hidude = hidude
#asyncio.coroutine
def i_call_other_coroutines(self):
return (yield from self.hidude.yeah_im_not_going_to_run())
class TestImBeingTested(unittest.TestCase):
def test_i_call_other_coroutines(self):
mocked = Mock(ImGoingToBeMocked)
ibt = ImBeingTested(mocked)
ret = asyncio.get_event_loop().run_until_complete(ibt.i_call_other_coroutines())

Since mock library doesn't support coroutines I create mocked coroutines manually and assign those to mock object. A bit more verbose but it works.
Your example may look like this:
import asyncio
import unittest
from unittest.mock import Mock
class ImGoingToBeMocked:
#asyncio.coroutine
def yeah_im_not_going_to_run(self):
yield from asyncio.sleep(1)
return "sup"
class ImBeingTested:
def __init__(self, hidude):
self.hidude = hidude
#asyncio.coroutine
def i_call_other_coroutines(self):
return (yield from self.hidude.yeah_im_not_going_to_run())
class TestImBeingTested(unittest.TestCase):
def test_i_call_other_coroutines(self):
mocked = Mock(ImGoingToBeMocked)
ibt = ImBeingTested(mocked)
#asyncio.coroutine
def mock_coro():
return "sup"
mocked.yeah_im_not_going_to_run = mock_coro
ret = asyncio.get_event_loop().run_until_complete(
ibt.i_call_other_coroutines())
self.assertEqual("sup", ret)
if __name__ == '__main__':
unittest.main()

I am writting a wrapper to unittest which aims at cutting the boilerplate when writting tests for asyncio.
The code lives here: https://github.com/Martiusweb/asynctest
You can mock a coroutine with asynctest.CoroutineMock:
>>> mock = CoroutineMock(return_value='a result')
>>> asyncio.iscoroutinefunction(mock)
True
>>> asyncio.iscoroutine(mock())
True
>>> asyncio.run_until_complete(mock())
'a result'
It also works with the side_effect attribute, and an asynctest.Mock with a spec can return CoroutineMock:
>>> asyncio.iscoroutinefunction(Foo().coroutine)
True
>>> asyncio.iscoroutinefunction(Foo().function)
False
>>> asynctest.Mock(spec=Foo()).coroutine
<class 'asynctest.mock.CoroutineMock'>
>>> asynctest.Mock(spec=Foo()).function
<class 'asynctest.mock.Mock'>
All the features of unittest.Mock are expected to work correctly (patch(), etc).

Springing off of Andrew Svetlov's answer, I just wanted to share this helper function:
def get_mock_coro(return_value):
#asyncio.coroutine
def mock_coro(*args, **kwargs):
return return_value
return Mock(wraps=mock_coro)
This lets you use the standard assert_called_with, call_count and other methods and attributes a regular unittest.Mock gives you.
You can use this with code in the question like:
class ImGoingToBeMocked:
#asyncio.coroutine
def yeah_im_not_going_to_run(self):
yield from asyncio.sleep(1)
return "sup"
class ImBeingTested:
def __init__(self, hidude):
self.hidude = hidude
#asyncio.coroutine
def i_call_other_coroutines(self):
return (yield from self.hidude.yeah_im_not_going_to_run())
class TestImBeingTested(unittest.TestCase):
def test_i_call_other_coroutines(self):
mocked = Mock(ImGoingToBeMocked)
mocked.yeah_im_not_going_to_run = get_mock_coro()
ibt = ImBeingTested(mocked)
ret = asyncio.get_event_loop().run_until_complete(ibt.i_call_other_coroutines())
self.assertEqual(mocked.yeah_im_not_going_to_run.call_count, 1)

You can create asynchronous mocks yourself:
import asyncio
from unittest.mock import Mock
class AsyncMock(Mock):
def __call__(self, *args, **kwargs):
sup = super(AsyncMock, self)
async def coro():
return sup.__call__(*args, **kwargs)
return coro()
def __await__(self):
return self().__await__()

A slightly simplified example for python 3.6+ adapted from a few of the answers here:
import unittest
class MyUnittest()
# your standard unittest function
def test_myunittest(self):
# define a local mock async function that does what you want, such as throw an exception. The signature should match the function you're mocking.
async def mock_myasync_function():
raise Exception('I am testing an exception within a coroutine here, do what you want')
# patch the original function `myasync_function` with the one you just defined above, note the usage of `wrap`, which hasn't been used in other answers.
with unittest.mock.patch('mymodule.MyClass.myasync_function', wraps=mock_myasync_function) as mock:
with self.assertRaises(Exception):
# call some complicated code that ultimately schedules your asyncio corotine mymodule.MyClass.myasync_function
do_something_to_call_myasync_function()

You can use asynctest and import CoroutineMock or use asynctest.mock.patch

Dustin's answer is probably the right one in the vast majority of cases. I had a different issue where the coroutine needed to return more than one value, e.g. simulating a read() operation, as briefly described in my comment.
After some more testing, the code below worked for me, by defining an iterator outside the mocking function, effectively remembering the last value returned to send the next one:
def test_some_read_operation(self):
#...
data = iter([b'data', b''])
#asyncio.coroutine
def read(*args):
return next(data)
mocked.read = Mock(wraps=read)
# Here, the business class would use its .read() method which
# would first read 4 bytes of data, and then no data
# on its second read.
So, expanding on Dustin's answer, it would look like:
def get_mock_coro(return_values):
values = iter(return_values)
#asyncio.coroutine
def mock_coro(*args, **kwargs):
return next(values)
return Mock(wraps=mock_coro)
The two immediate downsides I can see in this approach are:
It doesn't allow for raising exceptions easily (e.g. first return some data, then raise an error on second read operation).
I haven't found a way to use the standard Mock .side_effect or .return_value attributes to make it more obvious and readable.

Well, there are a bunch of answers here already, but I'll contribute my expanded version of e-satis's answer. This class mocks an async function and tracks call count and call args, just like the Mock class does for sync functions.
Tested on Python 3.7.0.
class AsyncMock:
''' A mock that acts like an async def function. '''
def __init__(self, return_value=None, return_values=None):
if return_values is not None:
self._return_value = return_values
self._index = 0
else:
self._return_value = return_value
self._index = None
self._call_count = 0
self._call_args = None
self._call_kwargs = None
#property
def call_args(self):
return self._call_args
#property
def call_kwargs(self):
return self._call_kwargs
#property
def called(self):
return self._call_count > 0
#property
def call_count(self):
return self._call_count
async def __call__(self, *args, **kwargs):
self._call_args = args
self._call_kwargs = kwargs
self._call_count += 1
if self._index is not None:
return_index = self._index
self._index += 1
return self._return_value[return_index]
else:
return self._return_value
Example usage:
async def test_async_mock():
foo = AsyncMock(return_values=(1,2,3))
assert await foo() == 1
assert await foo() == 2
assert await foo() == 3

You can subclass Mock to act like a coroutine function:
class CoroMock(Mock):
async def __call__(self, *args, **kwargs):
return super(CoroMock, self).__call__(*args, **kwargs)
def _get_child_mock(self, **kw):
return Mock(**kw)
You can use CoroMock pretty much like a normal mock, with the caveat that calls will not be recorded until the coroutine is executed by an event loop.
If you have a mock object and you want to make a particular method a coroutine, you can use Mock.attach_mock like this:
mock.attach_mock(CoroMock(), 'method_name')

New in Python 3.8 AsyncMock
Setting the spec of a Mock, MagicMock, or AsyncMock to a class with
asynchronous and synchronous functions will automatically detect the
synchronous functions and set them as MagicMock (if the parent mock is
AsyncMock or MagicMock) or Mock (if the parent mock is Mock). All
asynchronous functions will be AsyncMock.
class ExampleClass:
def sync_foo():
pass
async def async_foo():
pass
a_mock = AsyncMock(ExampleClass)
a_mock.sync_foo
>>> <MagicMock name='mock.sync_foo' id='...'>
a_mock.async_foo
>>> <AsyncMock name='mock.async_foo' id='...'>
mock = Mock(ExampleClass)
mock.sync_foo
>>> <Mock name='mock.sync_foo' id='...'>
mock.async_foo
>>> <AsyncMock name='mock.async_foo' id='...'>
Special Attributes for AsyncMock:
assert_awaited
assert_awaited_once
assert_awaited_with
assert_awaited_once_with
assert_any_await
assert_has_awaits
assert_not_awaited
reset_mock
await_count
await_args
await_args_list

You can fake type of AsyncMock
import asyncio
from types import CoroutineType
from unittest.mock import AsyncMock
task = AsyncMock(spec=CoroutineType)
asyncio.iscoroutine(task) # True

Related

How to mock f from with open as f being passed to a function? [duplicate]

I don't understand why I can't mock NamedTemporaryFile.name in this example:
from mock import Mock, patch
import unittest
import tempfile
def myfunc():
with tempfile.NamedTemporaryFile() as mytmp:
return mytmp.name
class TestMock(unittest.TestCase):
#patch('tempfile.NamedTemporaryFile')
def test_cm(self, mock_tmp):
mytmpname = 'abcde'
mock_tmp.__enter__.return_value.name = mytmpname
self.assertEqual(myfunc(), mytmpname)
Test results in:
AssertionError: <MagicMock name='NamedTemporaryFile().__enter__().name' id='140275675011280'> != 'abcde'
You are setting the wrong mock: mock_tmp is not the context manager, but instead returns a context manager. Replace your setup line with:
mock_tmp.return_value.__enter__.return_value.name = mytmpname
and your test will work.
To expand on Nathaniel's answer, this code block
with tempfile.NamedTemporaryFile() as mytmp:
return mytmp.name
effectively does three things
# Firstly, it calls NamedTemporaryFile, to create a new instance of the class.
context_manager = tempfile.NamedTemporaryFile()
# Secondly, it calls __enter__ on the context manager instance.
mytmp = context_manager.__enter__()
# Thirdly, we are now "inside" the context and can do some work.
return mytmp.name
When you replace tempfile.NamedTemporaryFile with an instance of Mock or MagicMock
context_manager = mock_tmp()
# This first line, above, will call mock_tmp().
# Therefore we need to set the return_value with
# mock_tmp.return_value
mytmp = context_manager.__enter__()
# This will call mock_tmp.return_value.__enter__() so we need to set
# mock_tmp.return_value.__enter__.return_value
return mytmp.name
# This will access mock_tmp.return_value.__enter__.return_value.name
Extending Peter K's answer using pytest and the mocker fixture.
def myfunc():
with tempfile.NamedTemporaryFile(prefix='fileprefix') as fh:
return fh.name
def test_myfunc(mocker):
mocker.patch('tempfile.NamedTemporaryFile').return_value.__enter__.return_value.name = 'tempfilename'
assert myfunc() == 'tempfilename'
Here is an alternative with pytest and mocker fixture, which is a common practice as well:
def test_myfunc(mocker):
mock_tempfile = mocker.MagicMock(name='tempfile')
mocker.patch(__name__ + '.tempfile', new=mock_tempfile)
mytmpname = 'abcde'
mock_tempfile.NamedTemporaryFile.return_value.__enter__.return_value.name = mytmpname
assert myfunc() == mytmpname
I extended hmobrienv's answer to a small working program
import tempfile
import pytest
def myfunc():
with tempfile.NamedTemporaryFile(prefix="fileprefix") as fh:
return fh.name
def test_myfunc(mocker):
mocker.patch("tempfile.NamedTemporaryFile").return_value.__enter__.return_value.name = "tempfilename"
assert myfunc() == "tempfilename"
if __name__ == "__main__":
pytest.main(args=[__file__])
Another possibility is to use a factory to create an object that implements the context manager interface:
import unittest
import unittest.mock
import tempfile
def myfunc():
with tempfile.NamedTemporaryFile() as mytmp:
return mytmp.name
def mock_named_temporary_file(tmpname):
class MockNamedTemporaryFile(object):
def __init__(self, *args, **kwargs):
self.name = tmpname
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
pass
return MockNamedTemporaryFile()
class TestMock(unittest.TestCase):
#unittest.mock.patch("tempfile.NamedTemporaryFile")
def test_cm(self, mock_tmp):
mytmpname = "abcde"
mock_tmp.return_value = mock_named_temporary_file(mytmpname)
self.assertEqual(myfunc(), mytmpname)

Mocking decorator which is outside of python class but class using this

I try to look answer on StackOverflow but I can not find a solution - the most answer is old or not used for my.
I have code like this:
#file app.py
#structure of directory
#-words
#--x
#---y
#----z
#------app.py
#-test
#--test.py
def is_connected(f):
def is_con(self):
if self.connection:
print("not work")
else:
return False
f()
return True
return is_con
class A():
/*another code */
#is_connected
def get_element(self):
return True
This is the basic logic of my code, for now, I want to test this using unittest.
def lol():
print(123)
from unittest.mock import patch
from words.x.y.z.app import A
class TestUnit:
#patch('words.x.y.z.app.is_connected)
def test_get(self, mocked):
mocked.return_value = 23
mocked.side_effect = lol
a = A()
assert a.get_element()
But when I run test I see in console
not work
So I guess, this decorator is not mocked. How I can do this properly?
The solutions from stackoverflow which I tried because have sense for me.:
Mock authentication decorator in unittesting ,
Can I patch a Python decorator before it wraps a function?
I found a solution: I added to my decorator #wraps(f) from functools import wraps
def is_connected(f):
def is_con(self):
if self.connection:
print("not work")
else:
return False
f()
return True
return is_con
And when I want to test any method with this I just do this:
from unittest.mock import patch
from words.x.y.z.app import A
class TestUnit:
def test_get(self, ):
a = A()
assert a.get_element.__wrapped__(a)
But I don't know how I should use this in this case:
#is_connected
def get_element(self, element):
return element
Should works sth like this:
assert a.get_element(1).__wrapped__(a)
but no

How to make a python mocked out function return a specific value conditional on an argument to the function?

I have a python 2.7x Tornado application that when run serves up a handful of RESTful api endpoints.
My project folder includes numerous test cases that rely on the python mock module such as shown below.
from tornado.testing import AsyncHTTPTestCase
from mock import Mock, patch
import json
from my_project import my_model
class APITestCases(AsyncHTTPTestCase):
def setUp(self):
pass
def tearDown(self):
pass
#patch('my_project.my_model.my_method')
def test_something(
self,
mock_my_method
):
response = self.fetch(
path='http://localhost/my_service/my_endpoint',
method='POST',
headers={'Content-Type': 'application/json'},
body=json.dumps({'hello':'world'})
)
The RESTful endpoint http://localhost/my_service/my_endpoint has two internal calls to my_method respectively: my_method(my_arg=1) and my_method(my_arg=2).
I want to mock out my_method in this test-case such that it returns 0 if it is called with my_arg==2, but otherwise it should return what it would always normally return. How can I do it?
I know that I should do something like this:
mock_my_method.return_value = SOMETHING
But I don't know how to properly specify that something so that its behavior is conditional on the arguments that my_method is called with. Can someone show me or point me to an example??
I want to mock out my_method in this test-case such that it returns 0 if it is called with my_arg==2, but otherwise it should return what it would always normally return. How can I do it?
Write your own method mock calling the original one on condition:
from my_project import my_model
my_method_orig = my_project.my_model.my_method
def my_method_mocked(self, *args, my_arg=1, **kwargs):
if my_arg == 2: # fake call
return 0
# otherwise, dispatch to real method
return my_method_orig(self, *args, **kwargs, my_arg=my_arg)
For patching: if you don't need to assert how often the mocked method was called and with what args etc, it is sufficient to pass the mock via new argument:
#patch('my_project.my_model.my_method', new=my_method_mocked)
def test_something(
self,
mock_my_method
):
response = self.fetch(...)
# this will not work here:
mock_my_method.assert_called_with(2)
If you want to invoke the whole mock assertion machinery, use side_effect as suggested in the other answer. Example:
#patch('my_project.my_model.my_method', side_effect=my_method_mocked, autospec=True)
def test_something(
self,
mock_my_method
):
response = self.fetch(...)
# mock is assertable here
mock_my_method.assert_called_with(2)
you could use side_effect to change return value dynamically:
class C:
def foo(self):
pass
def drive():
o = C()
print(o.foo(my_arg=1))
print(o.foo(my_arg=2))
def mocked_foo(*args, **kwargs):
if kwargs.get('my_arg') == 2:
return 0
else:
return 1
#patch('__main__.C.foo')
def test(mock):
mock.side_effect = mocked_foo
drive()
update: as you want to run original my_method code under some condition, you may need a method proxy, Mock can't get back the real function object being patched.
from unittest.mock import patch
class MyClass:
def my_method(self, my_arg):
return 10000
def func_wrapper(func):
def wrapped(*args, **kwargs):
my_arg = kwargs.get('my_arg')
if my_arg == 2:
return 0
return func(*args, **kwargs)
return wrapped
def drive(o, my_arg):
print('my_arg', my_arg, 'ret', o.my_method(my_arg=my_arg))
def test():
with patch.object(MyClass, 'my_method', new=func_wrapper(MyClass.my_method)):
o = MyClass()
drive(o, 1)
drive(o, 2)
will outputs:
my_arg 1 ret 10000
my_arg 2 ret 0

python monkey patching an object's method within a function via a decorator

I'm trying to learn some advanced decorator usage. Specifically, I'm trying to to monkey patch a class's method via a decorator within a function.
This is a basic example to illustrate what I'm trying to do. I have a function something that does some stuff; and within that function there's an instance of a class. That instance I would like to monkey patch.
from functools import update_wrapper
class Foobar:
def get_something(self):
return "apple"
class FakeFoobar:
def get_something(self):
return "orange"
class my_decorator:
def __init__(self, original_function):
self._original_function = original_function
update_wrapper(self, original_function)
def __call__(self, *args, **kwargs):
# some magic here?
return self._original_function(*args, **kwargs)
#my_decorator
def something():
f = Foobar()
return f.get_something()
if __name__ == '__main__':
print(something())
I'm trying either trying to do a 1 to 1 replacement with Foobar to FakeFoobar or, monkey patch Foobar's get_something method to FakeFoobar's get_something method.
When I run the code above, I get the following:
>>> something()
'apple'
>>>
I would like to find some way augment the Foobar's get_something method so that we get the following output:
>>> something()
'orange'
>>>
There's a mock module within the unittests library, however, it's not clear to be how I could leverage that for my use case. I'm fairly married to the idea of not passing an argument into the decorator or an extra argument into the something function as many of the examples of the mock library show.
I also notice that the moto library is accomplishing something similar to what I'm trying to do. I tried digging into the source code, but it seems fairly complex for what I'm trying to do.
How about updating the global variables dict of the function?
from functools import update_wrapper
class Foobar:
def get_something(self):
return "apple"
class FakeFoobar:
def get_something(self):
return "orange"
class my_decorator:
def __init__(self, original_function):
self._original_function = original_function
update_wrapper(self, original_function)
def __call__(self, *args, **kwargs):
f = self._original_function
restore_val = f.func_globals['Foobar']
f.func_globals['Foobar'] = f.func_globals['FakeFoobar']
# ^^^^^ This is your magic-line.
try:
return f(*args, **kwargs)
except:
raise
finally:
f.func_globals['Foobar'] = restore_val
#my_decorator
def something():
f = Foobar()
return f.get_something()
if __name__ == '__main__':
print(something()) #Prints orange
print(Foobar().get_something()) #Prints apple

Find Out If a Function has been Called

I am programming in Python, and I am wondering if i can test if a function has been called in my code
def example():
pass
example()
#Pseudocode:
if example.has_been_called:
print("foo bar")
How would I do this?
If it's OK for the function to know its own name, you can use a function attribute:
def example():
example.has_been_called = True
pass
example.has_been_called = False
example()
#Actual Code!:
if example.has_been_called:
print("foo bar")
You could also use a decorator to set the attribute:
import functools
def trackcalls(func):
#functools.wraps(func)
def wrapper(*args, **kwargs):
wrapper.has_been_called = True
return func(*args, **kwargs)
wrapper.has_been_called = False
return wrapper
#trackcalls
def example():
pass
example()
#Actual Code!:
if example.has_been_called:
print("foo bar")
A minimal example using unittest.mock.Mock from the standard library:
from unittest.mock import Mock
def example():
pass
example_mock = Mock(side_effect=example)
example_mock()
#Pseudocode:
if example_mock.called:
print("foo bar")
Console output after running the script:
foo bar
This approach is nice because it doesn't require you to modify the example function itself, which is useful if you want to perform this check in some unit-testing code, without modifying the source code itself (EG to store a has_been_called attribute, or wrap the function in a decorator).
Explanation
As described in the documentation for the unittest.mock.Mock class, the side_effect argument to the Mock() constructor specifies "a function to be called whenever the Mock is called".
The Mock.called attribute specifies "a boolean representing whether or not the mock object has been called".
The Mock class has other attributes you may find useful, EG:
call_count: An integer telling you how many times the mock object has been called
call_args: This is either None (if the mock hasn’t been called), or the arguments that the mock was last called with
call_args_list: This is a list of all the calls made to the mock object in sequence (so the length of the list is the number of times it has been called). Before any calls have been made it is an empty list
The Mock class also has convenient methods for making assert statements based on how many times a Mock object was called, and what arguments it was called with, EG:
assert_called_once_with(*args, **kwargs): Assert that the mock was called exactly once and that that call was with the specified arguments
We can use mock.Mock
from unittest import mock
def check_called(func):
return mock.Mock(side_effect=func)
#check_called
def summator(a, b):
print(a + b)
summator(1, 3)
summator.assert_called()
assert summator.called == True
assert summator.call_count > 0
summator.assert_called_with(1, 3)
summator.assert_called_with(1, 5) # error
# AssertionError: Expected call: mock(1, 5)
# Actual call: mock(1, 3)
Memoization functions have been around since the 1960s. In python you can use them as decorators on your example() function.
The standard memoization function looks something like this:
def memoize(func):
memo = {}
def wrapper(*args):
if not args in memo:
memo[args] = func(*args)
return memo[args]
return wrapper
and you decorate your function like this:
#memoize
def example():
pass
In python3.2, you can use the functools.lru_cache instead of the memoziation function.
import functools
#functools.lru_cache(maxsize=None)
def example():
pass
Here's a decorator that will watch all your functiona, using colorama, and return a nice output.
try:
import colorama
except ImportError:
class StdClass: pass
def passer(*args, **kwargs): pass
colorama = StdClass()
colorama.init = passer
colorama.Fore = StdClass()
colorama.Fore.RED = colorama.Fore.GREEN = ''
def check_for_use(show=False):
if show:
try:
check_for_use.functions
except AttributeError:
return
no_error = True
for function in check_for_use.functions.keys():
if check_for_use.functions[function][0] is False:
print(colorama.Fore.RED + 'The function {!r} hasn\'t been called. Defined in "{}" '.format(function, check_for_use.functions[function][1].__code__.co_filename))
no_error = False
if no_error:
print(colorama.Fore.GREEN + 'Great! All your checked function are being called!')
return check_for_use.functions
try:
check_for_use.functions
except AttributeError:
check_for_use.functions = {}
if colorama:
colorama.init(autoreset=True)
def add(function):
check_for_use.functions[function.__name__] = [False, function]
def func(*args, **kwargs):
check_for_use.functions[function.__name__] = [True, function]
function(*args, **kwargs)
return func
return add
#check_for_use()
def hello():
print('Hello world!')
#check_for_use()
def bonjour(nb):
print('Bonjour tout le monde!')
# hello(); bonjour(0)
hello()
check_for_use(True) # outputs the following
Output:
Hello world!
The function 'bonjour' hasn't been called. Defined in "path_to_file.py"
You can also create a variable and increment it in the function. Later you can check if it's 1 or >= 0.

Categories