I have a class ProductionClass with a method method_to_test which I want to test. Class ProductionClass has dependency to api, which I want to mock in the test.
from my_module.apis import api
class ProductionClass:
def method_to_test:
data = api.method_to_mock()
api.method_to_check_call(data)
The test code is as follows:
For api I have a mock class MockApi that I use by refering to it in the #patch decorator.
from unittest.mock import patch, MagicMock
class MockApi:
def method_to_mock():
return some_mock_data
def method_to_check_call(data):
pass
class TestClass:
#patch('my_module.apis.api', MagicMock(return_value=MockApi()))
def test_check_called_with(self):
from module_of_class_production_class.ProductionClass import method_to_test
mock_api = MockApi()
method_to_test()
some_data = { ... }
mock.method_to_check_call.assert_called_with(some_data)
The problem is that it does not work because mock_api is not the same instance of MockApi that is provided in the #patch decorator. Is there a better way to test that?
I have not tested this, but I think that your patch object will get passed as first argument to test_check_called_with like so:
#patch('my_module.apis.api', MagicMock(return_value=MockApi()))
def test_check_called_with(self, your_magic_mock):
# Rest of code
You can also use the with construct like so:
def test_check_called_with(self):
my_api = MockApi()
with patch('my_module.apis.api', MagicMock(return_value=my_api)) as my_mock_api:
# Your code here
You can checkout the official python documentation here for more details: https://docs.python.org/3/library/unittest.mock.html#quick-guide
Related
In this scenario:
In FileA:
from FileB import BJob
class EasyJob
def __init__():
self.job = BJob()
def launch_job_in_A():
self.job.launch_job_in_B(cpu=100, memory=200)
In FileB:
class BJob():
def __init__():
pass
def launch_job_in_B(cpu=0, memory=0):
do_some_thing(cpu, memory)
...
Is there a way to write a unit test for launch_job_in_A() function and also verify whether we do pass cpu=100 and memory=200 to launch_job_in_B() function successfully?
Broadly question is like, is it possible to check the parameter we passed to the function?
You can create a Mock via unittest.mock.patch and then use the assert_called_once_with method to verify the function call:
import unittest
from unittest import TestCase
from unittest.mock import patch
from FileA import EasyJob
class TestEasyJob(TestCase):
def test_launch_job_in_A(self):
with patch('FileB.BJob.launch_job_in_B') as mock:
job = EasyJob()
job.launch_job_in_A()
mock.assert_called_once_with(cpu=100, memory=200)
unittest.main()
Yes, you would replace self.job with a mock object and its launch_job_in_B with a mock method.
This would be easier if there was an option to pass in the BJob, rather than hard coding it. This avoids having to mock every call to launch_job_in_B which might have unexpected side effects. This also makes the class more flexible.
from FileB import BJob
class EasyJob
def __init__(bjob=BJob()):
self.job = bjob
def launch_job_in_A():
self.job.launch_job_in_B(cpu=100, memory=200)
Now we can mock the method just for a single object.
from FileA import EasyJob
from FileB import BJob
from unittest.mock import MagicMock
# Set up the mocked object and method.
mock_bjob = BJob()
# If it needs to return something, configure that.
mock_bjob.launch_job_in_B() = MagicMock()
# Make the EasyJob with the mocked BJob
ejob = EasyJob(bjob=mock_bjob)
# Call the method in question.
ejob.launch_job_in_A()
# Check if the method was called with the correct arguments.
mock_bjob.method.assert_called_once_with(cpu=100, memory=200)
BJob.launch_job_in_B would be tested in its own unit test.
I am used to pytest approach for unit testing, without using classes. Today I wanted to give a try to unittest and I wanted to encapsulate my tests inside a TestCase.
Then consider this sample test class:
import unittest
import moto
import boto3
class TestMyClass(unittest.TestCase):
#classmethod
#moto.mock_ssm
def setUpClass(cls) -> None:
cls.ssm_client = boto3.client('ssm')
cls.ssm_client.put_parameter(Name='test', Value='foo', Type='String')
#moto.mock_ssm
def test_something(self):
value = self.ssm_client.get_parameter(Name='test').get('Parameter').get('Value')
self.assertEqual(value, 'foo')
Why is not the parameter placed in setUpClass visible from the test? I could imagine that by using the #moto.mock_ssm decorator there it would all have been done in the mocked context.
I can, however, place the parameter within test_something as just:
#moto.mock_ssm
def test_something(self):
self.ssm_client.put_parameter(Name='test', Value='foo', Type='String')
value = self.ssm_client.get_parameter(Name='test').get('Parameter').get('Value')
self.assertEqual(value, 'foo')
And then it (obviously) works. Why not with my first approach? I do not want to be populating the fake ssm parameter for each test that will rely on it. What is the best way of doing so here?
The reason why I am asking this is because the class I want to test requires this parameter when it is initialised.
The mock-decorators are scoped for that particular function, so data created inside that function will only be available there.
Instead, you can use the class-decorator:
import unittest
import moto
import boto3
#moto.mock_ssm
class TestMyClass(unittest.TestCase):
client = None
def setUp(self) -> None:
self.client = boto3.client('ssm')
self.client.put_parameter(Name='test', Value='foo', Type='String')
def test_something(self):
value = self.client.get_parameter(Name='test').get('Parameter').get('Value')
self.assertEqual(value, 'foo')
Note that I've switched to setUp, instead of setUpClass. Because setUpClass is a classmethod, it is executed before the decorator is applied, and it will try to execute this method against AWS itself.
So,
consider I have a simple library that I am trying to write unit-tests for. This library talks to a database and then uses that data to call an SOAP API. I have three modules, and a testfile for each module.
dir structure:
./mypkg
../__init__.py
../main.py
../db.py
../api.py
./tests
../test_main
../test_db
../test_api
Code:
#db.py
import mysqlclient
class Db(object):
def __init__(self):
self._client = mysqlclient.Client()
#property
def data(self):
return self._client.some_query()
#api.py
import soapclient
class Api(object):
def __init__(self):
self._client = soapclient.Client()
#property
def call(self):
return self._client.some_external_call()
#main.py
from db import Db
from api import Api
class MyLib(object):
def __init__(self):
self.db = Db()
self.api = Api()
def caller(self):
return self.api.call(self.db.data)
Unit-Tests:
#test_db.py
import mock
from mypkg.db import Db
#mock.patch('mypkg.db.mysqlclient')
def test_db(mysqlclient_mock):
mysqlclient_mock.Client.return_value.some_query = {'data':'data'}
db = Db()
assert db.data == {'data':'data'}
#test_api.py
import mock
from mypkg.api import Api
#mock.patch('mypkg.db.soapclient')
def test_db(soap_mock):
soap_mock.Client.return_value.some_external_call = 'foo'
api = Api()
assert api.call == 'foo'
In the above example, mypkg.main.MyLib calls mypkg.db.Db() (uses third-party mysqlclient) and then mypkg.api.Api() (uses third-party soapclient)
I am using mock.patch to patch the third-party libraries to mock my db and api calls in test_db and test_api separately.
Now my question is, is it recommended to patch these external calls again in test_main OR simply patch db.Db and api.Api? (this example is pretty simple, but in larger libraries, the code becomes cumbersome when patching the external calls again or even using test helper functions that patch internal libraries).
Option1: patch external libraries in main again
#test_main.py
import mock
from mypkg.main import MyLib
#mock.patch('mypkg.db.mysqlclient')
#mock.patch('mypkg.api.soapclient')
def test_main(soap_mock, mysqlcient_mock):
ml = MyLib()
soap_mock.Client.return_value.some_external_call = 'foo'
assert ml.caller() == 'foo'
Option2: patch internal libraries
#test_main.py
import mock
from mypkg.main import MyLib
#mock.patch('mypkg.db.Db')
#mock.patch('mypkg.api.Api')
def test_main(api_mock, db_mock):
ml = MyLib()
api_mock.return_value = 'foo'
assert ml.caller() == 'foo'
mock.patch creates a mock version of something where it's imported, not where it lives. This means the string passed to mock.patch has to be a path to an imported module in the module under test. Here's what the patch decorators should look like in test_main.py:
#mock.patch('mypkg.main.Db')
#mock.patch('mypkg.main.Api')
Also, the handles you have on your patched modules (api_mock and db_mock) refer to the classes, not instances of those classes. When you write api_mock.return_value = 'foo', you're telling api_mock to return 'foo' when it gets called, not when an instance of it has a method called on it. Here are the objects in main.py and how they relate to api_mock and db_mock in your test:
Api is a class : api_mock
Api() is an instance : api_mock.return_value
Api().call is an instance method : api_mock.return_value.call
Api().call() is a return value : api_mock.return_value.call.return_value
Db is a class : db_mock
Db() is an instance : db_mock.return_value
Db().data is an attribute : db_mock.return_value.data
test_main.py should therefore look like this:
import mock
from mypkg.main import MyLib
#mock.patch('mypkg.main.Db')
#mock.patch('mypkg.main.Api')
def test_main(api_mock, db_mock):
ml = MyLib()
api_mock.return_value.call.return_value = 'foo'
db_mock.return_value.data = 'some data' # we need this to test that the call to api_mock had the correct arguments.
assert ml.caller() == 'foo'
api_mock.return_value.call.assert_called_once_with('some data')
The first patch in Option 1 would work great for unit-testing db.py, because it gives the db module a mock version of mysqlclient. Similarly, #mock.patch('mypkg.api.soapclient') belongs in test_api.py.
I can't think of a way Option 2 could help you unit-test anything.
Edited: I was incorrectly referring to classes as modules. db.py and api.py are modules
Im trying to patch multiple methods in a class. Here is my simplified set up
Hook.py is defined as
class Hook():
def get_key(self):
return "Key"
def get_value(self):
return "Value"
HookTransfer.py defined as
from Hook import Hook
class HookTransfer():
def execute(self):
self.hook = Hook()
key = self.hook.get_key()
value = self.hook.get_value()
print(key)
print(value)
I want to mock the methods get_key and get_value in the Hook class. The following works i.e. prints New_Key and New_Value
from HookTransfer import HookTransfer
import unittest
from unittest import mock
class TestMock(unittest.TestCase):
#mock.patch('HookTransfer.Hook.get_key', return_value="New_Key")
#mock.patch('HookTransfer.Hook.get_value', return_value="New_Value")
def test_execute1(self, mock_get_key, mock_get_value):
HookTransfer().execute()
if __name__ == '__main__':
unittest.main()
However this does not. It prints <MagicMock name='Hook().get_key()' id='4317706896'> and <MagicMock name='Hook().get_value()' id='4317826128'>
from HookTransfer import HookTransfer
import unittest
from unittest import mock
class TestMock(unittest.TestCase):
#mock.patch('HookTransfer.Hook', spec=True)
def test_execute2(self, mock_hook):
mock_hook.get_key = mock.Mock(return_value="New_Key")
mock_hook.get_value = mock.Mock(return_value="New_Value")
HookTransfer().execute()
if __name__ == '__main__':
unittest.main()
Intuitively it seems like the second one should work too but it doesnt. Could you help explain why it does not. I suspect it has something to do with "where to patch" but Im unable to get clarity.
You can patch multiple methods of a module or a class using patch.multiple(). Something like this should work for your case:
import unittest
from unittest.mock import MagicMock, patch
class TestMock(unittest.TestCase):
#patch.multiple('HookTransfer.Hook',
get_key=MagicMock(return_value='New_Key'),
get_value=MagicMock(return_value='New_Value'))
def test_execute1(self, **mocks):
HookTransfer().execute()
When patch.multiple() is used as a decorator, the mocks are passed into the decorated function by keyword, and a dictionary is returned when it's used as a context manager.
What you need to is:
mock the class Hook,
from HookTransfer import HookTransfer
from Hook import Hook
import unittest
try:
import mock
except ImportError:
from unittest import mock
class TestMock(unittest.TestCase):
#mock.patch.object(Hook, 'get_key', return_value="New_Key")
#mock.patch.object(Hook, 'get_value', return_value="New_Value")
def test_execute1(self, mock_get_value, mock_get_key):
HookTransfer().execute()
if __name__ == "__main__":
unittest.main()
After some testing I was able to find the issue.
In the second test case, the patch decorator creates a new instance of a Mock class and passes it via mock_hook argument to test_execute2 function. Lets refer to this as mock1. mock1 replaces the Hook class in HookTransfer.py. When self.hook = Hook() is run, it translates to calling __init__ of mock1. By design this returns yet another Mock instance - lets refer to this as mock2. So self.hook points to mock2. But mock_hook.get_key = mock.Mock(return_value="New_Key"), mocks the methods in mock1.
In order to mock correctly, mock2 needs to be patched. This can be done in 2 ways
By mocking the return_value of mock1 (which returns mock2) mock_hook.return_value.get_key = mock.Mock(return_value="New_Key")
Mocking the return value of constructor of mock1 (which returns mock2) mock_hook().get_key = mock.Mock(return_value="New_Key")
Under the wraps both options really do the same thing.
I am using mock with Python and was wondering which of those two approaches is better (read: more pythonic).
Method one: Just create a mock object and use that. The code looks like:
def test_one (self):
mock = Mock()
mock.method.return_value = True
self.sut.something(mock) # This should called mock.method and checks the result.
self.assertTrue(mock.method.called)
Method two: Use patch to create a mock. The code looks like:
#patch("MyClass")
def test_two (self, mock):
instance = mock.return_value
instance.method.return_value = True
self.sut.something(instance) # This should called mock.method and checks the result.
self.assertTrue(instance.method.called)
Both methods do the same thing. I am unsure of the differences.
Could anyone enlighten me?
mock.patch is a very very different critter than mock.Mock. patch replaces the class with a mock object and lets you work with the mock instance. Take a look at this snippet:
>>> class MyClass(object):
... def __init__(self):
... print 'Created MyClass#{0}'.format(id(self))
...
>>> def create_instance():
... return MyClass()
...
>>> x = create_instance()
Created MyClass#4299548304
>>>
>>> #mock.patch('__main__.MyClass')
... def create_instance2(MyClass):
... MyClass.return_value = 'foo'
... return create_instance()
...
>>> i = create_instance2()
>>> i
'foo'
>>> def create_instance():
... print MyClass
... return MyClass()
...
>>> create_instance2()
<mock.Mock object at 0x100505d90>
'foo'
>>> create_instance()
<class '__main__.MyClass'>
Created MyClass#4300234128
<__main__.MyClass object at 0x100505d90>
patch replaces MyClass in a way that allows you to control the usage of the class in functions that you call. Once you patch a class, references to the class are completely replaced by the mock instance.
mock.patch is usually used when you are testing something that creates a new instance of a class inside of the test. mock.Mock instances are clearer and are preferred. If your self.sut.something method created an instance of MyClass instead of receiving an instance as a parameter, then mock.patch would be appropriate here.
I've got a YouTube video on this.
Short answer: Use mock when you're passing in the thing that you want mocked, and patch if you're not. Of the two, mock is strongly preferred because it means you're writing code with proper dependency injection.
Silly example:
# Use a mock to test this.
my_custom_tweeter(twitter_api, sentence):
sentence.replace('cks','x') # We're cool and hip.
twitter_api.send(sentence)
# Use a patch to mock out twitter_api. You have to patch the Twitter() module/class
# and have it return a mock. Much uglier, but sometimes necessary.
my_badly_written_tweeter(sentence):
twitter_api = Twitter(user="XXX", password="YYY")
sentence.replace('cks','x')
twitter_api.send(sentence)
Key points which explain difference and provide guidance upon working with unittest.mock
Use Mock if you want to replace some interface elements(passing args) of the object under test
Use patch if you want to replace internal call to some objects and imported modules of the object under test
Always provide spec from the object you are mocking
With patch you can always provide autospec
With Mock you can provide spec
Instead of Mock, you can use create_autospec, which intended to create Mock objects with specification.
In the question above the right answer would be to use Mock, or to be more precise create_autospec (because it will add spec to the mock methods of the class you are mocking), the defined spec on the mock will be helpful in case of an attempt to call method of the class which doesn't exists ( regardless signature), please see some
from unittest import TestCase
from unittest.mock import Mock, create_autospec, patch
class MyClass:
#staticmethod
def method(foo, bar):
print(foo)
def something(some_class: MyClass):
arg = 1
# Would fail becuase of wrong parameters passed to methd.
return some_class.method(arg)
def second(some_class: MyClass):
arg = 1
return some_class.unexisted_method(arg)
class TestSomethingTestCase(TestCase):
def test_something_with_autospec(self):
mock = create_autospec(MyClass)
mock.method.return_value = True
# Fails because of signature misuse.
result = something(mock)
self.assertTrue(result)
self.assertTrue(mock.method.called)
def test_something(self):
mock = Mock() # Note that Mock(spec=MyClass) will also pass, because signatures of mock don't have spec.
mock.method.return_value = True
result = something(mock)
self.assertTrue(result)
self.assertTrue(mock.method.called)
def test_second_with_patch_autospec(self):
with patch(f'{__name__}.MyClass', autospec=True) as mock:
# Fails because of signature misuse.
result = second(mock)
self.assertTrue(result)
self.assertTrue(mock.unexisted_method.called)
class TestSecondTestCase(TestCase):
def test_second_with_autospec(self):
mock = Mock(spec=MyClass)
# Fails because of signature misuse.
result = second(mock)
self.assertTrue(result)
self.assertTrue(mock.unexisted_method.called)
def test_second_with_patch_autospec(self):
with patch(f'{__name__}.MyClass', autospec=True) as mock:
# Fails because of signature misuse.
result = second(mock)
self.assertTrue(result)
self.assertTrue(mock.unexisted_method.called)
def test_second(self):
mock = Mock()
mock.unexisted_method.return_value = True
result = second(mock)
self.assertTrue(result)
self.assertTrue(mock.unexisted_method.called)
The test cases with defined spec used fail because methods called from something and second functions aren't complaint with MyClass, which means - they catch bugs, whereas default Mock will display.
As a side note there is one more option: use patch.object to mock just the class method which is called with.
The good use cases for patch would be the case when the class is used as inner part of function:
def something():
arg = 1
return MyClass.method(arg)
Then you will want to use patch as a decorator to mock the MyClass.