exec python script and mock missing modules - python

I am running a execfile() on python script but in this script there are calls to modules that I don't have. I would therefore like to replace them with fakeObject/mock
I have this instruction:
import mymodule.mtest.core as CCORE
API = CCORE.object()
API.initialize(sys.argv, comm=comm)
But i dont have mymodule.mtest.core and i want to replace CCORE.object() with a fake object
containing the initialize() method
I tried someting like this:
sys.modules['mymodule'] = MyfakeObj
With MyFakeObj is a python module with a fake def initialiaze() method
But i have this error
ModuleNotFoundError: No module named 'mymodule.mtest'; 'mymodule' is not a
package
How can i do this ?

thank you for your help. I tested your proposal but in my context the instructions
ith patch.object(mymodule.mtest.core, "object", return_value=Mock()):
does not works,
on the other hand it put me on the track to use Mock()
I use like this:
sys.modules['mymodule'] = MagicMock()
sys.modules['mymodule.mtest'] = MagicMock()
sys.modules['mymodule.mtest.core'] = MagicMock()
And i can exec my script with the instructions
import mymodule.mtest.core
the import no longer crashes

You can patch/mock objects using unittest.mock. But because unittest was created for testing purposes, I'm really not sure if you should use it.
from unittest.mock import Mock, patch
with patch("mymodule.mtest.core"):
execfile() # What you want to run
If you want to get custom value from mymodule.mtest.core attribute or method:
from unittest.mock import Mock, patch
with patch("mymodule.mtest.core") as mocked_function:
mocked_function = Mock()
mocked_function.some_attribute= "something"
mocked_function.some_method = Mock(return_value="something")
execfile() # What you want to run

Related

Pytest mocker failing to find Path

I am working with someone else's testing code, and they make extensive use of mocker. The problem is that I changed the underlying code so it tests for the existence of a file using Path ().is_file.
Now I need to mock Path ().is_file so it returns True. I tried this:
from pathlib import Path
#pytest.fixture(scope="function")
def mock_is_file (mocker):
# mock the AlignDir existence validation
mocker.patch ('Path.is_file')
return True
I'm getting this error:
E ModuleNotFoundError: No module named 'Path'
/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/mock.py:1161: ModuleNotFoundError
What is the correct way to patch Path.is_file()?
Mock will import the object given the string, so you need to patch with a fully qualified name.
You should probably also set the return_value so that the call returns a boolean, rather than returning another generated mock. The return value in the fixture itself is not needed.
mocker.patch("pathlib.Path.is_file", return_value=True)
You also don't need to import the thing that you're mocking at the top of the test module like that, mock itself will import it when patching.

Mocking an entire function with parameters in Python

I have this Python code normally running on a distant server that I want to test locally:
from af.data import Variable
def create_file():
main_client = file.Client()
directory = main_client.create(Variable.get("DIR_NAME"))
As I am developing locally and do not have access to the remote service providing the af.data.Variable class, I'd like to mock the Variable.get(str) function, but I'd like to be able—in my own mock—to return some value based on the passed the str parameter. So far, I've found only ways to mock a function to some pre-defined static values using side_effect of unittest.
How can I do that?
after the Peter's comment, here is the solution (pretty straightforward):
from unittest.mock import Mock
from af.data import Variable
import os
def side_effect(arg):
"""Put here whatever logic you want to put in your mock, here's an example of reading a system environment variable"""
return os.getenv(arg)
mock = Mock()
mock.get.side_effect = side_effect
Variable = mock
"""Then just use the Variable.get method as you would do in production environment, it should be mocked to the side_effect function declared above"""

How to test python class which calls a missing module?

I try to test python class with pytest.
I have python module like so:
import module_A
from module_b import module_c
class classToTest():
def foo():
return module_c.func() + 1
def bar():
return module_A.func() + 2
So, how to properly test bar() function if I don’t have any modules imported in the instance above?
I decided to mock patch module_A.func(), but patched object should be imported before.
I’ve created a fake module_A locally and do so:
import module_A
from mock import patch
#patch(‘module_A.func’, return_value=10)
def test_bar()
MyClass = ClassToTest()
assert MyClass.bar() == 12
I have an import error because I don’t have module_b installed. How to solve it? Probably it’s beter to reorganize the code to make it more testable or I should use another approaches to build a good test?
something like this may help, but you have to position it before your import of whatever holds classToTest.
import sys
try:
import module_b
except (ImportError,) as e:
from unittest.mock import Mock
module_b = Mock()
module_b.module_c = module_b
def func():
return 10
module_b.func = func
sys.modules["module_b"] = module_b
#import your code under test
With self.assertEqual(classToTest().foo(), 12) I got an asserterror that 11 <> 12, it wasn't happy with the numbers not matching, so the fake module_b was accepted.
Please note, I was only playing around with faking out a module_b import. The whole test structure probably has issues, as Code-Apprentice pointed out.
2nd warning: I would not do this to somehow fake out an installed module_b not being installed. system.modules, once it has that mock in it registered, will need tidying up. reload ought to do it, but who knows? in that case, the usual mock/patch might yield better results.

Unable to mock a function in Python (Django)

I'm trying to mock a function used within the function I'm testing, but for some reason I'm not seeing the original function always runs, not the one I created as a mock.
The code I'm testing has this type of setup:
def function_being_tested(foo):
...
function_i_want_to_mock(bar)
...
def function_i_want_to_mock(bar)
print("Inside original function")
...
I've installed Mock and I've tried using unittest.mock patch
Currently the test file uses this setup:
import mock
from django.test import TestCase
def mock_function_i_want_to_mock(bar):
print(bar)
return True
class SupportFunctionsTestCases(TestCase):
#mock.patch("path.to.function.function_i_want_to_mock", mock_function_i_want_to_mock)
def test_function_being_tested(self):
# setup
result = function_being_tested(test_foo)
self.assertEqual(result, expected_result)
What then happens is when I run the test, I always get: "Inside original function", not the parameter printed so it's always running the original function.
I've used this exact setup before and it has worked so I'm not sure what's causing this. Probably some wrong setup...
If anyone has a different way of doing this or spots some error, it would be appreciated.
"path.to.function.function_i_want_to_mock" should be a path to where the function is used, not defined.
So if function_i_want_to_mock is defined in moduleA.py but imported and used in moduleB.py which you are testing then you should use #mock.patch("path.to.moduleB.function_i_want_to_mock", ...).

How to mock an import

Module A includes import B at its top. However under test conditions I'd like to mock B in A (mock A.B) and completely refrain from importing B.
In fact, B isn't installed in the test environment on purpose.
A is the unit under test. I have to import A with all its functionality. B is the module I need to mock. But how can I mock B within A and stop A from importing the real B, if the first thing A does is import B?
(The reason B isn't installed is that I use pypy for quick testing and unfortunately B isn't compatible with pypy yet.)
How could this be done?
You can assign to sys.modules['B'] before importing A to get what you want:
test.py:
import sys
sys.modules['B'] = __import__('mock_B')
import A
print(A.B.__name__)
A.py:
import B
Note B.py does not exist, but when running test.py no error is returned and print(A.B.__name__) prints mock_B. You still have to create a mock_B.py where you mock B's actual functions/variables/etc. Or you can just assign a Mock() directly:
test.py:
import sys
sys.modules['B'] = Mock()
import A
The builtin __import__ can be mocked with the 'mock' library for more control:
# Store original __import__
orig_import = __import__
# This will be the B module
b_mock = mock.Mock()
def import_mock(name, *args):
if name == 'B':
return b_mock
return orig_import(name, *args)
with mock.patch('__builtin__.__import__', side_effect=import_mock):
import A
Say A looks like:
import B
def a():
return B.func()
A.a() returns b_mock.func() which can be mocked also.
b_mock.func.return_value = 'spam'
A.a() # returns 'spam'
Note for Python 3:
As stated in the changelog for 3.0, __builtin__ is now named builtins:
Renamed module __builtin__ to builtins (removing the underscores, adding an ‘s’).
The code in this answer works fine if you replace __builtin__ by builtins for Python 3.
How to mock an import, (mock A.B)?
Module A includes import B at its top.
Easy, just mock the library in sys.modules before it gets imported:
if wrong_platform():
sys.modules['B'] = mock.MagicMock()
and then, so long as A doesn't rely on specific types of data being returned from B's objects:
import A
should just work.
You can also mock import A.B:
This works even if you have submodules, but you'll want to mock each module. Say you have this:
from foo import This, That, andTheOtherThing
from foo.bar import Yada, YadaYada
from foo.baz import Blah, getBlah, boink
To mock, simply do the below before the module that contains the above is imported:
sys.modules['foo'] = MagicMock()
sys.modules['foo.bar'] = MagicMock()
sys.modules['foo.baz'] = MagicMock()
(My experience: I had a dependency that works on one platform, Windows, but didn't work on Linux, where we run our daily tests.
So I needed to mock the dependency for our tests. Luckily it was a black box, so I didn't need to set up a lot of interaction.)
Mocking Side Effects
Addendum: Actually, I needed to simulate a side-effect that took some time. So I needed an object's method to sleep for a second. That would work like this:
sys.modules['foo'] = MagicMock()
sys.modules['foo.bar'] = MagicMock()
sys.modules['foo.baz'] = MagicMock()
# setup the side-effect:
from time import sleep
def sleep_one(*args):
sleep(1)
# this gives us the mock objects that will be used
from foo.bar import MyObject
my_instance = MyObject()
# mock the method!
my_instance.method_that_takes_time = mock.MagicMock(side_effect=sleep_one)
And then the code takes some time to run, just like the real method.
Aaron Hall's answer works for me.
Just want to mention one important thing,
if in A.py you do
from B.C.D import E
then in test.py you must mock every module along the path, otherwise you get ImportError
sys.modules['B'] = mock.MagicMock()
sys.modules['B.C'] = mock.MagicMock()
sys.modules['B.C.D'] = mock.MagicMock()
I realize I'm a bit late to the party here, but here's a somewhat insane way to automate this with the mock library:
(here's an example usage)
import contextlib
import collections
import mock
import sys
def fake_module(**args):
return (collections.namedtuple('module', args.keys())(**args))
def get_patch_dict(dotted_module_path, module):
patch_dict = {}
module_splits = dotted_module_path.split('.')
# Add our module to the patch dict
patch_dict[dotted_module_path] = module
# We add the rest of the fake modules in backwards
while module_splits:
# This adds the next level up into the patch dict which is a fake
# module that points at the next level down
patch_dict['.'.join(module_splits[:-1])] = fake_module(
**{module_splits[-1]: patch_dict['.'.join(module_splits)]}
)
module_splits = module_splits[:-1]
return patch_dict
with mock.patch.dict(
sys.modules,
get_patch_dict('herp.derp', fake_module(foo='bar'))
):
import herp.derp
# prints bar
print herp.derp.foo
The reason this is so ridiculously complicated is when an import occurs python basically does this (take for example from herp.derp import foo)
Does sys.modules['herp'] exist? Else import it. If still not ImportError
Does sys.modules['herp.derp'] exist? Else import it. If still not ImportError
Get attribute foo of sys.modules['herp.derp']. Else ImportError
foo = sys.modules['herp.derp'].foo
There are some downsides to this hacked together solution: If something else relies on other stuff in the module path this kind of screws it over. Also this only works for stuff that is being imported inline such as
def foo():
import herp.derp
or
def foo():
__import__('herp.derp')
I found fine way to mock the imports in Python. It's Eric's Zaadi solution found here which I just use inside my Django application.
I've got class SeatInterface which is interface to Seat model class.
So inside my seat_interface module I have such an import:
from ..models import Seat
class SeatInterface(object):
(...)
I wanted to create isolated tests for SeatInterface class with mocked Seat class as FakeSeat. The problem was - how tu run tests offline, where Django application is down. I had below error:
ImproperlyConfigured: Requested setting BASE_DIR, but settings are not
configured. You must either define the environment variable
DJANGO_SETTINGS_MODULE or call settings.configure() before accessing
settings.
Ran 1 test in 0.078s
FAILED (errors=1)
The solution was:
import unittest
from mock import MagicMock, patch
class FakeSeat(object):
pass
class TestSeatInterface(unittest.TestCase):
def setUp(self):
models_mock = MagicMock()
models_mock.Seat.return_value = FakeSeat
modules = {'app.app.models': models_mock}
patch.dict('sys.modules', modules).start()
def test1(self):
from app.app.models_interface.seat_interface import SeatInterface
And then test magically runs OK :)
.
Ran 1 test in 0.002s
OK
If you do an import ModuleB you are really calling the builtin method __import__ as:
ModuleB = __import__('ModuleB', globals(), locals(), [], -1)
You could overwrite this method by importing the __builtin__ module and make a wrapper around the __builtin__.__import__method. Or you could play with the NullImporter hook from the imp module. Catching the exception and Mock your module/class in the except-block.
Pointer to the relevant docs:
docs.python.org: __import__
Accessing Import internals with the imp Module
I hope this helps. Be HIGHLY adviced that you step into the more arcane perimeters of python programming and that a) solid understanding what you really want to achieve and b)thorough understanding of the implications is important.
I know this is a fairly old question, but I have found myself returning to it a few times recently, and wanted to share a concise solution to this.
import sys
from unittest import mock
def mock_module_import(module):
"""Source: https://stackoverflow.com/a/63584866/3972558"""
def _outer_wrapper(func):
def _inner_wrapper(*args, **kwargs):
orig = sys.modules.get(module) # get the original module, if present
sys.modules[module] = mock.MagicMock() # patch it
try:
return func(*args, **kwargs)
finally:
if orig is not None: # if the module was installed, restore patch
sys.modules[module] = orig
else: # if the module never existed, remove the key
del sys.modules[module]
return _inner_wrapper
return _outer_wrapper
It works by temporarily patching the key for the module in sys.modules, and then restoring the original module after the decorated function is called. This can be used in scenarios where a package may not be installed in the testing environment, or a more complex scenario where the patched module might actually perform some of its own internal monkey-patching (which was the case I was facing).
Here's an example of use:
#mock_module_import("some_module")
def test_foo():
# use something that relies upon "some_module" here
assert True
I found myself facing a similar problem today, and I've decided to solve it a bit differently. Rather than hacking on top of Python's import machinery, you can simply add the mocked module into sys.path, and have Python prefer it over the original module.
Create the replacement module in a subdirectory, e.g.:
mkdir -p test/mocked-lib
${EDITOR} test/mocked-lib/B.py
Before A is imported, insert this directory to sys.path. I'm using pytest, so in my test/conftest.py, I've simply done:
import os.path
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "mocked-lib"))
Now, when the test suite is run, the mocked-lib subdirectory is prepended into sys.path and import A uses B from mocked-lib.

Categories

Resources