Using mock.patch in Python - python

How can I patch a variable used by foo() and which is imported from another file?
test file:
from f import foo
def test():
foo()
f file:
from f2 import some_var
def foo():
print some_var
Even if some_var in f file is 10, I might want it to have another value, when foo() is called from test(). How can I achieve that using mock.patch.object?

Without trying it myself:
#test file
import f2
import mock
def test():
with mock.patch('f2.some_var', 'your-new-value-for-somevar'):
# your test code
as you are importing some_var using from f2 import some_var in your "f file" then you'll need to make sure that the patch is in place when from f2 import some_var runs. I'd just use import f2 in "f file" instead, and refer to some_var as f2.some_var.
---edit---
Gah, you can of course just do this in your test class:
#test file
import f
import mock
def test():
with mock.patch('f.some_var', 'your-new-value-for-somevar'):
This will patch the value of some_var that has been copied into f by from f2 import some_var

You don't necessarily need patch.object for modifying values in the case you've given. You can just do:
test.py:
from f import foo
import f
f.some_var = 'test-val'
def test():
foo()
Foo will then print out 'test-val' in the case you've given.
If instead you have a function that needs to be mocked, you can use patch.object as a decorator in addition to Steve's example.
test.py
from mock import patch
from f import foo
import f
#patch.object(f, 'some_fn')
def test(some_fn_mock):
some_fn_mock.return_value = 'new-stuff'
f.foo()
test()
f.py
from f2 import some_fn
def foo():
print some_fn()
f2.py
def some_fn():
print 'stuff'

Related

Mock method of an object inside function [python]

I have two files:- file1.py and file2.py
file2.py has following code:-
import json
def fun1():
s = "{'function1': 'val1'}"
s = json.dumps(s)
print("in fun1 ", s)
return s
def fun2():
s = "{'function2': 'value2'}"
s = json.dumps(s)
print("in fun2 ", s)
return s
def fun5():
fun2()
return fun1()
file1.py has following code
from mockito import when, unstub
from file2 import fun5
def mock_the_function():
when("file2.fun1.json").dumps(...).thenReturn("something something")
print(fun5())
unstub()
I want to mock "dumps" inside "fun1" only, not "fun2". Code which I have written is showing error. I don't want to do it by parameter comparison. Is there any other way I can have a function being passed inside "when"?
First a quick note:
json.dumps takes an object, not a string, so it's kind of redundant to to call it as you are inside fun1 and fun2. Perhaps you're looking for json.loads?
Next I'd consider some different approaches:
When you mock json.dumps, you want to mock the the json property of the file2 module, so it would just be when("file2.json")... in the setup code for your test.
Because (as above), we're mocking the json module globally in the file2 module, it's not possible to, as you asked, in that context of a single test, mock json.dumps inside of fun1 but not fun2, but what I'd suggest is to simply have two tests, and unstub in a tear down method. For example:
from unittest import TestCase
class TestExample(TestCase):
def tearDown(self):
unstub()
def test_fun1(self):
when("file2.json").dumps(...).thenReturn("something something")
# in this invocation json is stubbed
self.assertEqual(fun1(), "something something")
def test_fun2(self):
# but now unstub has been called so it won't be stubbed anymore.
self.assertEqual(fun2(), "...output of fun2...")
Another alternative is for fun1 and fun2 to take a function that will do the work that the global json module is currently doing. This use of the Dependency Inversion Principle makes the code more testable, and means you don't even need mockito in your tests. For example:
def fun1(json_decoder):
s = "..."
return json_decoder(s)
# ....
from unittest.mock import MagicMock
def test_fun_1():
mock_decoder = MagicMock()
mock_decoder.return_value = "asdf"
assert fun1(mock_decoder) == "asdf"

Avoid duplicate name when importing a sub-module with only one public member from a package

Basic Setup
Suppose I want to create a class named Foo. I may create a file like so:
foo.py:
class Foo:
def __init__(self):
self.data = "world"
def print(self):
print("Hello, " + self.data)
To utilize this class in my main script:
main.py
import foo
test = foo.Foo()
test.print()
Having to type foo.Foo() every time I instantiate the class already feels ridiculous enough, but it gets worse when I want to organize my code by separating my classes into a package:
classes/__init__.py
# Empty
classes/foo.py
# Copy of foo.py, above
main.py
import classes.foo
test = classes.foo.Foo()
test.print()
Simple Answer
I know I can clean this up somewhat by using from X import Y like so:
from classes.foo import Foo
test = Foo()
Preferred Answer
Because the file foo.py contains only one member whose name matches the file, I would prefer if I could do something like the following:
from classes import Foo
# Or:
import classes.Foo as Foo
test = Foo()
Is there a way to do this? Maybe with some code in my __init__.py?
In classes/__init__.py, put:
from .foo import Foo
Now you can write from classes import Foo.

Python - unittest, mock, patch, input

so i've got a problem with my code.
File 1:
class Abc(object):
...
def function1(self):
#do something
def function2(self):
x = input()
return x+1
and now i'm trying to test function 2 so i wrote a test for it and i don't know what i am doing wrong:
from unittest.mock import patch
import unittest
from file1 import *
class TestBackend(unittest.TestCase):
def test_mode_first(self):
self.assertEqual(Abc().funcion1(), 30)
#patch('funcion2.input', create=True)
def test_mode_second(self, mocked_input):
mocked_input.side_effect = ["QWE"]
result = Abc().funcion2()
self.assertEqual(result, 10)
if __name__ == '__main__':
unittest.main()
i get ModuleNotFoundError: No module named 'function2'
so what i am doing wrong in here?
thanks for your help :)
You get ModuleNotFoundError because funcion2 is not a module. patch doc is clear about this:
target should be a string in the form 'package.module.ClassName'. The
target is imported and the specified object replaced with the new
object, so the target must be importable from the environment you are
calling patch() from. The target is imported when the decorated
function is executed, not at decoration time.
This works for me when executed with python3 -m unittest discover from the directory the files are in.
BTW you have a couple of typos in your example, e.g. Abc().funcion2(), note the missing t in funcion2.
Also, try not to use from … import *: https://docs.quantifiedcode.com/python-anti-patterns/maintainability/from_module_import_all_used.html#using-wildcard-imports-from-import
# file1.py
class Abc(object):
def function1(self):
return 30
def function2(self):
x = input()
return x + "1"
# test_file1.py
import unittest
from unittest.mock import patch
from file1 import Abc
class TestBackend(unittest.TestCase):
def test_mode_first(self):
self.assertEqual(Abc().function1(), 30)
#patch('builtins.input')
def test_mode_second(self, mocked_input):
mocked_input.return_value = "QWE"
result = Abc().function2()
self.assertEqual(result, "QWE1")

Subclass __init__ not seeing superclass conditonally-imported module

I have a conditional import in a self-initialized instance of a superclass, but subclass cannot see the module (python 2.7):
class A(object):
def __init__(self, arg1):
self.attr1 = self.method1(arg1)
def method1(self, arg1):
if arg1 == 'foo':
import amodule
return amodule.method1()
else:
return 'not a dependency on foo'
class B(A):
def __init__(self, arg1):
super(B, self).__init__(arg1)
if arg1 == 'foo':
self.attr2 = self.method2(self.attr1)
def method2(self, attr1):
return amodule.method2()
if __name__=='__main__':
b = B("foo")
print b.attr2
This throws NameError: global name 'amodule' is not defined. a = A("foo") works just fine
Shouldn't the super call have executed import amodule in this case? (And using import should have put the module into globals?)
Doesn't import add /amodule/ to the global namespace of the currently
executing module? (__main__)?
No, the module is added to sys.modules but if it was imported locally then you won't have any references to it anymore. i.e the name amodule is now gone.
You could still access the module using sys.modules:
def method2(self, attr1):
import sys
return sys.modules['amodule'].method2()
Or you could import it using import amodule again and it will be picked up from sys.modules.
# Here b.py contains
# print('Module b was imported')
def func1():
print('inside func1')
import b
def func2():
print('inside func2')
import sys
print(sys.modules['b'])
import b
def func3():
print('inside func3')
import b
import sys
print('Deleted b')
del sys.modules['b']
import b
func1()
print()
func2()
print()
func3()
Demo:
inside func1
Module b was imported
inside func2
<module 'b' from '/Users/ashwini/py/b.py'>
inside func3
Deleted b
Module b was imported
Try putting import amodule on the first line of the program.
The reason is that amodule is imported in method1, method2 does not have access.
If you follow your code, you would see you don't reach method1().
When you create the object
b = B(foo)
You go through A.init() becuase the call for super(). However, your init of class A() doesn't include any import statements. Then the A.__init__ part is done, and you continue with B.__init__(). The next command is a call to amodule object, which wasn't imported at all.
You can add an helper method, which checks if the arg equals 'Foo' and if so import the module. Then add a call to this function in A.__init__() function.
On another note, __init__() job is to initialize variables. It shouldn't return anything.

How do I override imports of other files for unit testing

I am currently attempting to write unit tests for my Main.py's main() function
Here is a simplified version of my Main.py:
from Configuration import Configuration # Configuration.py is a file in the same dir
def main():
try:
Configuration('settings.ini')
except:
sys.exit(1) # Test path1
sys.exit(0) # Test path2
if __name__ == '__main__':
main()
In my Unit Tests\MainUnitTests.py I want to import ..\Main.py and fake the Configuration class in such a way that I can hit Test path1 and Test path2
I found that i can assert sys.exit() with the following:
with self.assertRaises(SystemExit) as cm:
main()
self.assertEqual(cm.exception.code, 1)
but I am having trouble overriding the from Configuration import Configuration
Thoughts?
So far I have tried the following within Unit Tests\MainUnitTests.py:
class FakeFactory(object):
def __init__(self, *a):
pass
sys.modules['Configuration'] = __import__('FakeFactory')
class Configuration(FakeFactory):
pass
Another example for demonstration:
foo.py:
from bar import a,b
x = a()
class a(object):
def __init__(self):
self.q = 2
y = a()
print x.q, y.q # prints '1 2' as intended
b() # I want this to print 2 without modifying bar.py
bar.py:
class a(object):
def __init__(self):
self.q = 1
def b():
t = a()
print t.q
when you use the import
from bar import a
it import the name directly into the module, so monkeypatching bar won't help, you need to override a directly in the main file:
def fake_a():
print("OVERRIDEN!")
main.a = fake_a
Do know that unittest has helper functions for this in the mock subpackage, I believe you could do something like:
from unittest.mock import patch
...
with patch("main.a", fake_a) as mock_obj: #there are additional things you can do with the mock_obj
do_stuff()
This would work in your first example with configuration since the class that needs to be patched is not used in the global scope although foo uses bar.a as soon as it is loaded so you would need to patch it before even loading foo:
from unittest.mock import patch
...
with patch("bar.a", fake_a) as mock_obj: #there are additional things you can do with the mock_obj
import foo #now when it loads it will be loaded with the patched name
However in this case foo.a would not be reverted at the end of the with block because it can't be caught by unittest... I really hope your actual use case doesn't use the stuff to be patched at module level.

Categories

Resources