Mock method of an object inside function [python] - 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"

Related

Python: Getting mocked function value returned, not MagicMock, without adding returnvalue

So, basically I want to mock a function imported in another class, and for some reason I can't retrieve the mocked result without calling returnvalue of the mock.
This is the setup: one file, one module, one test class. I want to mock functionB() in source.fileB.
source.fileB
def functionB():
print('toto')
source.fileA
from source.fileB import *
def functionA():
print("bar")
return functionB()
Test case
from source.fileA import functionA
from source.fileA import functionB
#mock.patch('source.fileA.functionB')
def test_functionA(functionB_mock):
functionB_mock().returnvalue = "foo"
print(functionB_mock) # prints MagicMock object named functionB
print(functionB_mock.returnvalue) # prints MagicMock object name functionB.returnvalue
print(functionB_mock().returnvalue) #prints "foo"
print(functionA().returnvalue) #executes functionA, prints both "bar" and "foo"
print(functionA()) #executes functionA, prints both "bar" and MagicMock object named functionB()
So every time I try to get the result of the mocked functionB(), I have to use returnvalue. This is driving me nuts as I cannot update functionA() with
return functionB().returnvalue
in order for the rest of the code under test to execute properly.
I must be doing something wrong, but I can't understand what precisely.
There are two problems:
The mock attribute you want is return_value, not returnvalue!
You need to set that attribute on the mock itself, not the result of calling the mock (which is a different mock).
Here's a self-contained (single-file) version of your test code with the fix and some explanatory comments.
import mock
def functionB():
print('toto')
def functionA():
print("bar")
return functionB()
#mock.patch('__main__.functionB')
def test_functionA(functionB_mock):
functionB_mock.return_value = "foo"
# Prints a mock object because that's what functionB_mock is.
print(functionB_mock)
# Prints "foo" because that's what functionB_mock returns.
print(functionB_mock())
# The following two lines would raise AttributeError because
# "foo" isn't a Mock and doesn't have a 'return_value' attribute!
# print(functionB_mock().return_value)
# print(functionA().return_value)
# Executes functionA, prints "bar" and "foo"
print(functionA())
test_functionA()

How to mock using sys.modules and with mock.patch (Python interference on static functions)

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

Python unittest: Unable to mock imported functions so that conditional evaluates to False

I'm encountering a problem with unit testing in Python. Specifically, when I try to mock a function my code imports, variables assigned to the output of that function get assigned to a MagicMock object instead of the mock-function's return_value. I've been digging through the docs for python's unittest library, but am not having any luck.
The following is the code I want to test:
from production_class import function_A, function_B, function_M
class MyClass:
def do_something(self):
variable = functionB()
if variable:
do_other_stuff()
else:
do_something_else
this is what I've tried:
#mock.patch(path.to.MyClass.functionB)
#mock.patch(<other dependencies in MyClass>)
def test_do_something(self, functionB_mock):
functionB_mock.return_value = None # or False, or 'foo' or whatever.
myClass = MyClass()
myClass.do_something()
self.assertTrue(else_block_was_executed)
The issue I have is that when the test gets to variable = functionB in MyClass, the variable doesn't get set to my return value; it gets set to a MagicMock object (and so the if-statement always evaluates to True). How do I mock an imported function such that when executed, variables actually get set to the return value and not the MagicMock object itself?
We'd have to see what import path you're actually using with path.to.MyClass.functionB. When mocking objects, you don't necessarily use the path directly to where the object is located, but the one that the intepreter sees when recursively importing modules.
For example, if your test imports MyClass from myclass.py, and that file imports functionB from production_class.py, the mock path would be myclass.functionB, instead of production_class.functionB.
Then there's the issue that you need additional mocks of MyClass.do_other_stuff and MyClass.do_something_else in to check whether MyClass called the correct downstream method, based on the return value of functionB.
Here's a working example that tests both possible return values of functionB, and whether they call the correct downstream method:
myclass.py
from production_class import functionA, functionB, functionM
class MyClass:
def do_something(self):
variable = functionB()
if variable:
self.do_other_stuff()
else:
self.do_something_else()
def do_other_stuff(self):
pass
def do_something_else(self):
pass
production_class.py
import random
def functionA():
pass
def functionB():
return random.choice([True, False])
def functionM():
pass
test_myclass.py
import unittest
from unittest.mock import patch
from myclass import MyClass
class MyTest(unittest.TestCase):
#patch('myclass.functionB')
#patch('myclass.MyClass.do_something_else')
def test_do_something_calls_do_something_else(self, do_something_else_mock, functionB_mock):
functionB_mock.return_value = False
instance = MyClass()
instance.do_something()
do_something_else_mock.assert_called()
#patch('myclass.functionB')
#patch('myclass.MyClass.do_other_stuff')
def test_do_something_calls_do_other_stuff(self, do_other_stuff_mock, functionB_mock):
functionB_mock.return_value = True
instance = MyClass()
instance.do_something()
do_other_stuff_mock.assert_called()
if __name__ == '__main__':
unittest.main()
calling python test_myclass.py results in:
..
----------------------------------------------------------------------
Ran 2 tests in 0.002s
OK
What I wound up doing was changing the import statements in MyClass to import the object instead of the individual methods. I was then able to mock the object without any trouble.
More explicitly I changed MyClass to look like this:
import production_class as production_class
class MyClass:
def do_something(self):
variable = production_class.functionB()
if variable:
do_other_stuff()
else:
do_something_else
and changed my test to
#mock.patch(path.to.MyClass.production_class)
def test_do_something(self, prod_class_mock):
prod_class_mock.functionB.return_value = None
myClass = MyClass()
myClass.do_something()
self.assertTrue(else_block_was_executed)

(pytest) Why doesn't property mock work in fixture?

I have a class with some properties. In my test, I need to set up a fixture, and have the properties mocked. However, the patch only works in the fixture function, not when the fixture is called. Any idea how to fix this?
Here is the simplified version of the problem. Let's assume that this is my class Panda:
class Panda(object):
def __init__(self, name):
self.panda_name = name
#property
def name(self):
return self.panda_name
and this is my test
import pytest
from unittest.mock import patch, PropertyMock
from tmp import Panda
#pytest.fixture
#patch(
'tmp.Panda.name',
new_callable=PropertyMock,
return_value="yuanyuan")
def fixture_panda(mk_name):
p = Panda("this name should not matter")
print(p.name) # print "yuanyuan"
return p
def test_panda_fixture(fixture_panda):
p = fixture_panda
print(p.name) # print "this name should not matter"
# this test fails
assert p.name == "yuanyuan"
The first print function in fixture_panda would print yuanyuan, which means the propertyMock works as expected. However the 2nd print function in test_panda_fixture print this name should not matter, which means the propertyMock doesn't work here. Any idea why this happens and how to fix this?
If you want to monkeypatch something in pytest, you can use their built-in fixture monkeypatch, which can be inserted into all fixtures with scope = function. Here is an example from my codebase:
#pytest.fixture(scope="function", autouse=True)
def no_jwt(monkeypatch):
"""Monkeypatch the JWT verification functions for tests"""
monkeypatch.setattr("flask_jwt_extended.verify_jwt_in_request", lambda: print("Verify"))
If I apply it to your example, I think something like this should work:
#pytest.fixture
def fixture_panda(monkeypatch, mk_name):
monkeypatch.setattr('tmp.Panda.name', "yuanyuan")
p = Panda("this name should not matter")
print(p.name) # print "yuanyuan"
return p
You have three problems. Firstly, you're patching the fixture function but you should be patching the test function. This is because the way you've written it, the assertion falls outside the scope of the patching.
Secondly you should ditch the superfluous mkname.
Thirdly, your return_value is in the wrong place; it needs to apply to the PropertyMock object which the patch returns, not as a parameter to the patching function. When using new_callable you need to set it within the test setup, e.g.:
#patch('tmp.Panda.name', new_callable=PropertyMock)
def test_panda_fixture(mock_name, fixture_panda):
mock_name.return_value = "yuanyuan"
...
However you can do it within the decorator by using new instead of new_callable. Here's a working version which shows that approach:
import pytest
from unittest.mock import patch, PropertyMock
from tmp import Panda
#pytest.fixture
def fixture_panda():
p = Panda("this name should not matter")
print(p.name) # print "yuanyuan"
return p
#patch('tmp.Panda.name', new=PropertyMock(return_value="yuanyuan"))
def test_panda_fixture(fixture_panda):
p = fixture_panda
print(p.name) # print "this name should not matter"
# this test fails
assert p.name == "yuanyuan"

Mock function from other module

I have two python files:
function.py:
def foo ():
return 20
def func ():
temp = foo()
return temp
and mocking.py:
from testing.function import *
import unittest
import mock
class Testing(unittest.TestCase):
def test_myTest(self):
with mock.patch('function.func') as FuncMock:
FuncMock.return_value = 'string'
self.assertEqual('string', func())
I want to mock func, but with no positive result. I have AssertionError: 'string' != 20. What should I do to mock it correctly ? If I do mock.patch ('func') I have TypeError: Need a valid target to patch. You supplied: 'func'. If I move func to mocking.py and call foo: function.foo() it works correctly. But how to do it when I don't want to move functions from function.py to mocking.py ?
Mocking is useful when you're calling an actual function but you want some function calls inside that function to be mocked out. In your case you're aiming to mock func and you wish to call that mocked function directly by doing func().
However that won't work because you're mocking function.func but you've imported func already into your test file. So the func() that you're calling is an actual function, it's not the same as the mocked FuncMock. Try calling FuncMock() and you'll get the result as expected.
The following should work and it gives you an idea of what can be done:
from testing.function import func
import unittest
import mock
class Testing(unittest.TestCase):
def test_myTest(self):
with mock.patch('testing.function.foo') as FooMock:
FooMock.return_value = 'string'
self.assertEqual('string', func())

Categories

Resources