Using unittests and moto to mock AWS - python

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.

Related

Python Unit Test Mock import & check assert called with

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

How to mock a method call in a global variable

I was checking this trying to assign a method call in a module-level variable with the intention of being executed only once, but not sure why before running any of my unit tests it will pass through all the global references of the module, the problem with this is that I have a third party method that I am assigning to a global variable and fails because is trying to execute the actual method in this first pass through, I see that this behaviour is the same even with a simple local method, here an example to replicate it, this is in a file called
project_name.app.py
def printing_values():
# this is corrected mocked, as I am using patch in unit test to
# mock this but only available in the context of the test but not
# globally
print('from mocked printing_values method', SSM_VALUE)
return SSM_VALUE
def get_ssm():
return "value_from_method"
# this line will execute get_ssm before any unit test,
# how mock this to always have a mock value
SSM_VALUE = get_ssm()
Here is my unit test
""" response_transformer TESTS """
import unittest
from unittest import mock
import project_name.app
class TestGlobalVariable(unittest.TestCase):
#mock.patch('project_name.app.SSM_VALUE', 'testing_value')
def test_success_response_global_variable(self):
response = project_name.app.printing_values()
assert response == "testing_value"
So I would like to mock SSM_VALUE but not executing the get_ssm method associated with it, how should I achieve this?
""" response_transformer TESTS """
import unittest from unittest import mock
import project_name.app
class TestGlobalVariable(unittest.TestCase):
#mock.patch('project_name.app.printing_values')
def test_success_response_global_variable(self, mock_printing_values):
mock_printing_values.return_value = 'testing_value'
response = project_name.app.printing_values()
assert response == "testing_value"

How can I mock any function which is not being called directly?

TL;DR
How can I patch or mock "any functions that are not being called/used directly"?
Sceneario
I have a simple unit-test snippet as
# utils/functions.py
def get_user_agents():
# sends requests to a private network and pulls data
return pulled_data
# my_module/tasks.py
def create_foo():
from utils.functions import get_user_agents
value = get_user_agents()
# do something with value
return some_value
# test.py
class TestFooKlass(unittest.TestCase):
def setUp(self):
create_foo()
def test_foo(self):
...
Here in setUp() method I am calling get_user_agents() function indirectly by calling create_foo(). During this execution I have got socket.timeout exception since get_user_agents() tried to access a private network. So, how can I manipulate the return data or the entire get_user_agents function during the test?
Also, is there any way to persists this mock/patch during the whole test suite execution?
It does not matter that you call the function indirectly - important is to patch it as it is imported. In your example you import the function to be patched locally inside the tested function, so it will only be imported at function run time. In this case you have to patch the function as imported from its module (e.g. 'utils.functions.get_user_agents'):
class TestFooKlass(unittest.TestCase):
def setUp(self):
self.patcher = mock.patch('utils.functions.get_user_agents',
return_value='foo') # whatever it shall return in the test
self.patcher.start() # this returns the patched object, i case you need it
create_foo()
def tearDown(self):
self.patcher.stop()
def test_foo(self):
...
If you had imported the function instead at module level like:
from utils.functions import get_user_agents
def create_foo():
value = get_user_agents()
...
you should have patched the imported instance instead:
self.patcher = mock.patch('my_module.tasks.get_user_agents',
return_value='foo')
As for patching the module for all tests: you can start patching in setUp as shown above, and stop it in tearDown.

Test instance of class that mocked method is called from in Python

I am mocking out a method of a class and want to test the instance of the class that the method was called from to test that the creation part of my function works as expected.
In my particular case do_stuff tries to write bar_instance to an Excel File and I don't want that to happen i.e.
def create_instance(*args):
return Bar(*args)
class Bar():
def __init__(self, *args):
self.args = args
def do_stuff(self):
pass
def foo(*args):
bar_instance = create_instance(*args)
bar_instance.do_stuff()
Then in a testing file
from unittest import TestCase
from unittest.mock import patch
from path.to.file import foo
class TestFoo(TestCase):
#patch('path.to.file.Bar.do_stuff')
def test_foo(self, mock_do_stuff):
test_args = [1]
_ = foo(*test_args)
# Test here the instance of `Bar` that `mock_do_stuff` was called from
# Something like
actual_args = list(bar_instance.args)
self.assertEqual(test_args, actual_args)
I put a break in the test function after foo(*test_args) is run but can't see any way from the mocked method of accessing the instance of Bar it was called from and am a bit stuck. I don't want to mock out Bar further up the code as I want to make sure the correct instance of Bar is being created.
In your code example, there are three things that might need testing: function create_instance, class Bar and function foo. I understand your test code such that you want to ensure that function foo calls do_stuff on the instance returned by create_instance.
Since the original create_instance function has control over the created instance, a solution of your problem is to mock create_instance such that your test gains control of the object that is handed over to foo:
import unittest
from unittest import TestCase
from unittest.mock import patch, MagicMock
from SO_60624698 import foo
class TestFoo(TestCase):
#patch('SO_60624698.create_instance')
def test_foo_calls_do_stuff_on_proper_instance (
self, create_instance_mock ):
# Setup
Bar_mock = MagicMock()
create_instance_mock.return_value = Bar_mock
# Exercise
foo(1, 2, 3) # args are irrelevant
# Verify
Bar_mock.do_stuff.assert_called()
if __name__ == '__main__':
unittest.main()
In addition, you might also want to test if foo passes the arguments correctly to create_instance. This could be implemented as a separate test:
...
#patch('SO_60624698.create_instance')
def test_foo_passes_arguments_to_create_instance (
self, create_instance_mock ):
# Setup
create_instance_mock.return_value = MagicMock()
# Exercise
foo(1, 22, 333)
# Verify
create_instance_mock.assert_called_with(1, 22, 333)
And, certainly, to complete the whole test of the object generation, you could test create_instance directly, by calling it and checking on the returned instance of Bar if it has used its arguments correctly for the construction of the Bar instance.
As patch returns an instance of Mock (or actually MagicMock, but it inherits the relevant methods from its base - Mock), you have the assert_called_with method available, which should do the trick.
Note that this method is sensitive to args/kwargs - you have to assert the exact same call.
Another note: it might be a better practice to use patch.object instead of patch here

Best practices using python mock for testing functions within sub modules

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

Categories

Resources