I'm trying to mock a method in a parent class that is declared out of any class method. The problem es I can't figure out how to make the LoggerUtils class to be instantiated in the parent class. Puting it inside an __init__ is not an option due to the large size of the implementation and the cost of refactor.
Is there a way to mock the parent class prior to the import of the tested class?
At least mock the method before is loaded when importing the class.
Is there any approach to solve the problem of non-lazy methods loading on import?
I tried lazy loading TensorFlow LazyLoad Library methods but I just didnt get it to work; patching all methods with mock library, but the methods always load before I can mock anything. Below I have an example of the mocking tries, but LoggerUtils is always called.
Parent Class:
abstract_api.py
class AbstractApi:
logger = LoggerUtils.get_logger('AbstractApi')
def update(self):
<code>
Class to test:
api_map_asset_type.py
from abstract_api import AbstractApi
class ApiMapAssetType(AbstractApi):
def update(self):
<code>
Test Class:
test_api_map_asset_type.py
from unittest import TestCase
from mock imort patch
from api_map_asset_type import ApiMapAssetType
class TestApiMapAssetType(TestCase):
#patch('api_map_asset_type.AbstractApi.LoggerUtils')
#patch('api_map_asset_type.AbstractApi')
def setUp(self, mock2_abstract_loger, mock_3):
self.asset_api = ApiMapAssetType()
#patch('AbstractApi.update')
def test_update(self, mock_parent_update):
mock_orm = MagicMock()
self.asset_api.update()
mock_parent_update.assert_called_with()
EDITED
This is the only solution I found, since I am not able to mock parent classes or mock methods in class attributes before being imported, I decided to mock the whole test before importing, yet I think this is not an optimal or clean solution:
Test Class:
test_api_map_asset_type.py
from undetermined_project_library.LoggerUtils import LoggerUtils
with patch.object(LoggerUtils, 'get_logger') as mock_logger:
from unittest import TestCase
from mock imort patch
from api_map_asset_type import ApiMapAssetType
class TestApiMapAssetType(TestCase):
def setUp(self):
self.asset_api = ApiMapAssetType()
#patch('AbstractApi.update')
def test_update(self, mock_parent_update):
mock_orm = MagicMock()
self.asset_api.update()
mock_parent_update.assert_called_with()
Provided I understood your module layout, this should work:
class TestApiMapAssetType(TestCase):
#patch('undetermined_project_library.LoggerUtils')
def setUp(self, mock_abstract_logger):
self.asset_api = ApiMapAssetType()
#patch('api_map_asset_type.ApiMapAssetType.update')
def test_update(self, mock_parent_update):
self.asset_api.update()
mock_parent_update.assert_called_with()
Note a couple of things:
you have to mock LoggerUtils from the library they are defined in; you have used AbstractApi.LoggerUtils, which is not correct, as LoggerUtils does not belong to AbstractApi
I removed the mock for AbstractApi - at least for this you don't need it
update is patched for the method that is actually called - using the base class method may make sense if you call the base implementation (don't know if you do)
I guessed some of your module layout - you may have to adapt it
You can do this by using the sys module in python. You can register the imports with sys module and replace the implementation with Mock or MagicMock instances.
This is how it looks like in code -
import sys
from unittest.mock import MagicMock
sys.modules['numpy'] = MagicMock()
sys.modules['pandas'] = MagicMock()
sys.modules['torch'] = MagicMock()
sys.modules['torch.nn'] = MagicMock()
sys.modules['torch.nn.functional'] = MagicMock()
sys.modules['dgl'] = MagicMock()
sys.modules['dgl.ops'] = MagicMock()
sys.modules['dgl.function'] = MagicMock()
from path.to.module_under_test import ClassUnderTest
Assuming module_under_test.py starts like -
import numpy as np
import pandas as pd
import torch
import dgl
import torch.nn.functional as F
from dgl.ops import edge_softmax
class ClassUnderTest:
# code
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.
So I've this code that mocks two times, the first time by mocking imports with:
sys.modules['random'] = MagicMock()
The second time happens inside the unittest of a function that used that import, for example a function that used random
The tests. py is:
import sys
import unittest
from unittest import mock
from unittest.mock import MagicMock
import foo
sys.modules['random'] = MagicMock()
class test_foo(unittest.TestCase):
def test_method(self):
with mock.patch('random.choice', return_value = 2):
object = foo.FooClass(3)
self.assertEqual(2, object.method(), 'Should be 2')
def test_staticmethod(self):
with mock.patch('random.choice', return_value = 2):
object = foo.FooClass(3)
self.assertEqual(2, object.method(), 'should be 2')
The original file Foo.py is:
import random
class FooClass:
def __init__(self,arg):
self.arg = arg
def method(self):
print(random.choice)
return random.choice([1,2,3])
#staticmethod
def staticmethod():
print(random.choice)
random.choice([1,2,3])
The two mocks contrarrest each other, and the mocking of random doesn't happen.
When it prints random it actually prints:
<<bound method Random.choice of <random.Random object at 0x7fe688028018>>
I want that to print a MagicMock.
Can someone help me understand what's happening? Why are they contrarresting each other?
You don't need to update the module source with sys.modules['random'] = MagicMock() without this line it works fine <MagicMock name='choice' id='...'>. patch already does all the work for the isolated temporary updating the method. See more explanation in the docs - Where to patch
I'm trying to unit test a class which is derived from a base_class in an external module. In my dev/test environment I have not access to this external module, which means I have to somehow mock this base_class.
My test-code resides in a different file from the code I'm trying to test.
The problem can be summarized as follows:
my_class.py
import external_module
class MyClass(external_module.ExternalClass):
def test_method(self):
return "successful"
test_my_class.py
import sys
import unittest
from unittest.mock import MagicMock
sys.modules['external_module'] = MagicMock()
from my_class import MyClass
class TestMyClass(unittest.TestCase):
def test_first(self):
my_class = MyClass()
result = my_class.test_method()
self.assertEqual(result, "successful")
if __name__ == '__main__':
unittest.main()
Results
When running test_my_class.py the result are the following.
AssertionError: <MagicMock name='mock.ExternalClass.test_method()' id='140272215184664'> != 'successful'
Clearly since the external_module is mocked, even MyClass becomes an instance of a mock-object.
Similar posts
The problem is similar to as described in Python mock: mocking base class for inheritance, but has the difference that the base_class is from an external module.
Even How to mock a base class with python mock library show som similarities to my problem, though the solutions can not be directly applied.
Tries and failures
To get the import
import external_module
to work in my_class.py
sys.modules['external_module'] = MagicMock()
need to be set in test_my_class.py.
Though, this leads to that external_module.* becomes a Mock-instance.
You could create a helper module mocked_external_module, which can be imported from your tests and also contains a class base_class. Then you do the following in your test code:
import mocked_external_module
sys.modules['external_module'] = mocked_external_module
Plus, every method of your base_class that you need to mock you can create as a Mock or MagicMock.
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.
In order to avoid a circular import, I've been forced to define a function that looks like:
# do_something.py
def do_it():
from .helpers import do_it_helper
# do stuff
Now I'd like to be able to test this function, with do_it_helper patched over. If the import were a top level import,
class Test_do_it(unittest.TestCase):
def test_do_it(self):
with patch('do_something.do_it_helper') as helper_mock:
helper_mock.return_value = 12
# test things
would work fine. However, the code above gives me:
AttributeError: <module 'do_something'> does not have the attribute 'do_it_helper'
On a whim, I also tried changing the patch statement to:
with patch('do_something.do_it.do_it_helper') as helper_mock:
But that produced a similar error. Is there any way to mock this function, given the fact that I'm forced into importing it within the function where it's used?
You should mock out helpers.do_it_helper:
class Test_do_it(unittest.TestCase):
def test_do_it(self):
with patch('helpers.do_it_helper') as helper_mock:
helper_mock.return_value = 12
# test things
Here's an example using mock on os.getcwd():
import unittest
from mock import patch
def get_cwd():
from os import getcwd
return getcwd()
class MyTestCase(unittest.TestCase):
#patch('os.getcwd')
def test_mocked(self, mock_function):
mock_function.return_value = 'test'
self.assertEqual(get_cwd(), 'test')