Mock a Glue Job unit test case - python

I am in need to mock a glue job to check if it exists for a unit test case.
The following code is used for it.
class GlueJob:
"""Function mocking glue job."""
def custom_get_glue_job(self,glue_client):
"""Test Glue Job"""
response = glue_client.get_job(JobName="abc")
return response
import unittest
import sys
from unittest import mock
from mock import patch
class TestGlueJobs(unittest.TestCase):
"""Function mocking glue job"""
#patch.object(GlueJob, 'custom_get_glue_job',return_value=True)
def check_glue_job_exists(self, glue_job):
"""Function printing python version."""
session = boto3.session.Session()
glue_client = session.client('glue', region_name='us-west-1')
GlueJob.custom_get_glue_job(glue_client)
print("success")
glue_job = GlueJob
TestGlueJobs().check_glue_job_exists()
I am getting the following errors:
No value for argument 'glue_client' in unbound method call
No value for argument 'glue_job' in method call
I have passed on the values for both this argument. Not sure what I am missing here.
Thanks.

Related

Python mock testing: How do I alter an Object in a mocked method?

I'm trying to test this kind of method that alters my incoming object, but it needs to be mocked because I can't run the code in a unit test case (requires massive files that aren't in the repo). I want to mock method_to_mock so that it sets the file_wrapper_object.status to whatever I want for that test case, notice the method doesn't return anything.
def method_to_mock(file_wrapper_object):
try:
process(file_wrapper_object) #<—- I can't run this in a test case
file_wrapper_object.status = "COMPLETE"
except:
file_wrapper_object.status = "FAILED"
def TestProcessFileWrapperObject(self):
file_wrapper_object = create_wrapper_object(arbitrary_data)
method_to_mock(file_wrapper_object)
self.assertEqual(file_wrapper_object.status, "COMPLETE")
How can I mock a method that doesn't return anything but alters the incoming object?
You can use the side_effect to specify a custom behavior for the mock object. Here is an example:
import unittest
from unittest.mock import MagicMock
def TestProcessFileWrapperObject(self):
file_wrapper_object = create_wrapper_object(arbitrary_data)
my_method_mock = MagicMock(return_value=None)
def side_effect(file_wrapper_object):
file_wrapper_object.status = "COMPLETE"
my_method_mock.side_effect = side_effect
my_method = my_method_mock
my_method(file_wrapper_object)
self.assertEqual(file_wrapper_object.status, "COMPLETE")
Please check this for more details.

How to mock a class method that is called from another class with pytest_mock

In the below files I have
InternalDogWebhookResource which calls VisitOrchestrator.fetch_visit. I am attempting to write a test for InternalDogWebhookResource but mock VisitOrchestrator.fetch_visit since it is a network call.
I have tried the mock paths:
api.dog.handlers.internal.VisitOrchestrator.fetch_visit
api.dog.handlers.internal.InternalDogWebhookResource.VisitOrchestrator.fetch_visit
api.dog.handlers.internal.InternalDogWebhookResource.fetch_visit
and many others, but I am always getting AssertionError: assert None
I can confirm that the client.post in the test works because when i remove the mock asserts, i get a response back from the api which means fetch_visit is called.
How can I find the mocker.patch path?
api/dog/handlers/internal.py
from api.dog.helpers.visits import VisitOrchestrator
#api.route("/internal/dog/webhook")
class InternalDogWebhookResource():
def post(self) -> JsonResponse:
if event_type == EventType.CHANGE:
VisitOrchestrator.fetch_visit(event['visitId'])
return JsonResponse(status=204)
api/dog/helpers/visits.py
class VisitOrchestrator:
#classmethod
def fetch_visit(cls, visit_id: str) -> VisitModel:
# do stuff
return visit
tests/v0/dog/handlers/test_webhook.py
import pytest
from pytest_mock import MockerFixture
from api.dog.handlers.internal import InternalDogWebhookResource, EventType
from tests.v0.utils import url_for
def test_webhook_valid(client, config, mocker: MockerFixture):
visit_id = '1231231'
mock_object = mocker.patch(
'api.dog.handlers.internal.VisitOrchestrator.fetch_visit',
return_value=visit_id,
)
res = client.post(
url_for(InternalDogWebhookResource),
json={'blag': 'blargh'}
)
assert mock_object.assert_called_once()
You're doing the right things - your second approach is generally the way to go with mocks (mocking api.dog.handlers.internal.InternalDogWebhookResource.VisitOrchestrator.fetch_visit)
I would try to do the minimal test code function:
def test_webhook_valid(mocker):
mock_fetch_visit = mocker.MagicMock(name='fetch_visit')
mocker.patch('api.dog.handlers.internal.VisitOrchestrator.fetch_visit',
new=mock_fetch_visit)
InternalDogWebhookResource().post()
assert 1 == mock_fetch_visit.call_count
If this works for you - maybe the problem is with the client or other settings in your test method.

Testing a method call inside a function

I need to mock the object whose method I call in a function. The object is initialized inside the function and that's the problem. How to replace its implementation with mock?
Function code:
def handler (event, context):
"""Function, when call Yandex Server less"""
function_heandler = Handler(event=event, context=context)
response = function_heandler.run()
return response
Test code:
def test_main_call_handler():
with mock.patch('function.handler.Handler', new=mock.MagicMock()) as mock_handler:
handler({}, object())
mock_handler.run.assert_called()
And this, as expected, does not work. The function will be called in another module and I cannot pass the mock object there. Any ideas on how to fix this?
You should mock the class Handler instead.
Assuming Handler is imported from package.module, you can simply patch package.module.Handler:
def test_main_call_handler():
with mock.patch('package.module.Handler') as mock_handler:
handler({}, object())
mock_handler.run.assert_called()
One thing to remember when mocking, is that you mock (replace) the attribute of the tested module and not the called object.
To be concrete, let's say that your tested module is named using_my_handler_module.py and it looks like this (the first import line is important):
from my_handler_module import Handler
def handler(event, context):
"""Function, when call Yandex Server less"""
function_heandler = Handler(event=event, context=context)
response = function_heandler.run()
return response
Now, you need to mock the Handler attribute of using_my_handler_module, so when it's used, it's mocked.
So the test function would look like so:
import mock
from using_my_handler_module import handler
def test_main_call_handler():
with mock.patch('using_my_handler_module.Handler', new=mock.MagicMock()) as mock_handler:
handler({}, object())
mock_handler.return_value.run.assert_called()
Note we patch 'using_my_handler_module.Handler' .
Next, we check the assert called like this: mock_handler.return_value.run.assert_called()
The reason is that mock_handler.return_value is matching the return object of Handler(event=event, context=context) and you execute the run method on that object, so need to append .run.assert_called().
I myself find that mocking is confusing at times. This is why I wrote a helper library on top of pytest mock, called pytest-mock-generator. Here is how you can use it for your case:
def test_main_call_handler_using_pytest_mock_generator(mocker, mg):
mg.generate_uut_mocks(handler)
handler({}, object())
You use the mg (mock generator) fixture to generate the mocks for you - simply send the tested function as a parameter to mg.generate_uut_mocks. When you execute the test function this output would be printed and copied to your clipboard:
# mocked dependencies
mock_Handler = mocker.MagicMock(name='Handler')
mocker.patch('using_my_handler_module.Handler', new=mock_Handler)
Copy it to the beginning of your test and you now have:
def test_main_call_handler_using_pytest_mock_generator(mocker, mg):
# mocked dependencies
mock_Handler = mocker.MagicMock(name='Handler')
mocker.patch('using_mock_fixture.using_my_handler_module.Handler', new=mock_Handler)
handler({}, object())
Next, you want to generate the asserts section, so use another mg function called generate_asserts - send it your new mock:
def test_main_call_handler_using_pytest_mock_generator(mocker, mg):
# mocked dependencies
mock_Handler = mocker.MagicMock(name='Handler')
mocker.patch('using_mock_fixture.using_my_handler_module.Handler', new=mock_Handler)
handler({}, object())
mg.generate_asserts(mock_Handler)
When you execute your test now, you would get a bunch of generated asserts:
assert 1 == mock_Handler.call_count
mock_Handler.assert_called_once_with(context=_object_object_at_0x7f7fa60c2790_, event={})
mock_Handler.return_value.run.assert_called_once_with()
You don't need most of them, copy the last line into your test and the final version of your working test would be like this one:
def test_main_call_handler_using_pytest_mock_generator(mocker):
# mocked dependencies
mock_Handler = mocker.MagicMock(name='Handler')
mocker.patch('using_mock_fixture.using_my_handler_module.Handler', new=mock_Handler)
handler({}, object())
mock_Handler.return_value.run.assert_called_once_with()

How to test a function that uses a decorator cache.memoize

I'am having problems testing a function that has a decorator for caching:
#retry(stop=stop_after_attempt(3))
#cache.memoize(60)
def get_azure_machine_info(rg_name, machine_name, expand="instanceView"):
try:
compute_client = get_azure_compute_client()
return compute_client.virtual_machines.get(rg_name, machine_name, expand=expand)
except CloudError:
return None
My test:
#patch("dev_maintenance.machines.get_azure_compute_client")
def test_get_azure_machine_info(get_azure_compute_client):
cache.delete_memoized('get_azure_machine_info')
with app.app_context():
ret = get_azure_machine_info("rg1", "m1")
get_azure_compute_client.assert_called_once()
assert len(get_azure_compute_client.return_value.method_calls) == 1
assert (
ret == get_azure_compute_client.return_value.virtual_machines.get.return_value
)
get_azure_compute_client.return_value.virtual_machines.get.assert_called_once_with(
"rg1", "m1", expand="instanceView"
)
Before i used cache the test was working fine, but now i can't figure out what is going on here.
The error:
The cache is trying to do its thing with the MagicMock objects that patch is creating. And it's failing since it 'can't pickle class unitest.mock.MagicMock'
The simplest work-around would be to mock out the cache module in the test. You can look here for pointers: Can I patch a Python decorator before it wraps a function?
Do this in a setUp() fixture.

How to mock a boto3 client object/call

I'm trying to mock one particular boto3 function. My module, Cleanup, imports boto3. Cleanup also has a class, "cleaner". During init, cleaner creates an ec2 client:
self.ec2_client = boto3.client('ec2')
I want to mock the ec2 client method: desribe_tags(), which python says is:
<bound method EC2.describe_tags of <botocore.client.EC2 object at 0x7fd98660add0>>
the furthest I've gotten is importing botocore in my test file and trying:
mock.patch(Cleaner.botocore.client.EC2.describe_tags)
which fails with:
AttributeError: 'module' object has no attribute 'EC2'
How do I mock this method?
Cleanup looks like:
import boto3
class cleaner(object):
def __init__(self):
self.ec2_client = boto3.client('ec2')
The ec2_client object is the one that has the desribe_tags() method. It's a botocore.client.EC2 object, but I never directly import botocore.
I found a solution to this when trying to mock a different method for the S3 client
import botocore
from mock import patch
import boto3
orig = botocore.client.BaseClient._make_api_call
def mock_make_api_call(self, operation_name, kwarg):
if operation_name == 'DescribeTags':
# Your Operation here!
print(kwarg)
return orig(self, operation_name, kwarg)
with patch('botocore.client.BaseClient._make_api_call', new=mock_make_api_call):
client = boto3.client('ec2')
# Calling describe tags will perform your mocked operation e.g. print args
e = client.describe_tags()
Hope it helps :)
You should be mocking with respect to where you are testing. So, if you are testing your cleaner class (Which I suggest you use PEP8 standards here, and make it Cleaner), then you want to mock with respect to where you are testing. So, your patching should actually be something along the lines of:
class SomeTest(Unittest.TestCase):
#mock.patch('path.to.Cleaner.boto3.client', return_value=Mock())
def setUp(self, boto_client_mock):
self.cleaner_client = boto_client_mock.return_value
def your_test(self):
# call the method you are looking to test here
# simple test to check that the method you are looking to mock was called
self.cleaner_client.desribe_tags.assert_called_with()
I suggest reading through the mocking documentation which has many examples to do what you are trying to do

Categories

Resources