I am attempting to write a test for a function which calls an object's classmethod -- this classmethod goes on to return a new instance of that class.
There are plenty of examples of patching class attributes both here on stackoverflow and elsewhere but I am having difficulty understanding how to patch attribute/value such that I can test my function. I've referred to this answer.
Essentially I am trying to patch the attribute xxxx of the instance of Foo (within myFn) so that I can test/assert the subsequent value from its call to some_other_function()
The code below is standalone a 'runnable' of the problem: I'm getting an AttributeError: Foo doesn't have the attribute 'xxxx'
import time
import unittest
from unittest.mock import patch, PropertyMock
class Foo(object):
def __init__(self, xxxx):
"""long running task"""
time.sleep(5)
self.xxxx = xxxx
#classmethod
def get(cls):
"""also a long running task"""
time.sleep(5)
xxxx = 555
return cls(xxxx)
def myFn():
v = Foo.get().xxxx
# the patched `xxxx` should be 666 at this point
return some_other_function(v)
class Test(unittest.TestCase):
#patch('__main__.Foo', autospec=True)
def test_myFn(self, mock_Foo):
with patch('__main__.Foo.xxxx', new_callable=PropertyMock, return_value=666):
x = myFn()
self.assertEqual(x, 666)
if __name__ == '__main__':
unittest.main()
Very grateful for anyone's help!
You should use the create parameter that will force the creation of the attribute if it does not exist:
def test_myFn(self):
with patch('__main__.xxxx', new_callable=PropertyMock, create=True, return_value=666):
x = myFn()
self.assertEqual(666,x)
Related
I am Python newbie and trying to understand how to mock methods in UTs.
Here is my Test
test_module2.py
from module2 import A
import unittest
from unittest.mock import patch, MagicMock
class TestBulkLoad(unittest.TestCase):
#patch('simple_module.SimpleModuleClass')
def test_fun(self, a):
a.simpleFun = MagicMock(return_value=3)
testA = A()
testA.fun()
assert a.simpleFun.called
and I want to check that dependent SimpleModuleClass was mocked and invoked
module2.py
from simple_module import SimpleModuleClass
class A:
def fun(self):
print('I am from A class')
thing = SimpleModuleClass()
thing.simpleFun()
return -1
simple_module.py
class SimpleModuleClass:
def simpleFun(self):
print("I am from SimpleModuleClass")
return 0
I get AssertionError. Could you help me understand how to fix this?
a.simpleFun = MagicMock(return_value=3)
This line is only modifying the method of your mock object a (which you really should rename simpleModuleMock, since it's a mock of a SimpleModule class and not an A). It doesn't mock every instance of SimpleModule everywhere in your code, hence why an instance of SimpleModule is instantiated in A.fun() and not an instance of a mock of SimpleModule.
I would use dependency injection and pass an instance of SimpleModule to A.fun() instead of instantiating it inside of it, so that you can pass a mock. You could also make thing a public variable like so:
test_module2.py:
class TestBulkLoad(unittest.TestCase):
#patch('simple_module.SimpleModuleClass')
def test_fun(self, simpleModuleMock):
simpleModuleMock.simpleFun = MagicMock(return_value=3)
testA = A()
A.thing = simpleModuleMock
assert(testA.fun() == -1)
# assert simpleModule.simpleFun.called
assert isinstance(simpleModuleMock.simpleFun, MagicMock)
module2.py:
class A:
thing = SimpleModuleClass()
def fun(self):
print('I am from A class')
self.thing.simpleFun()
return -1
Note that there may exist better solutions with Python, but if there are, 1. I don't know of them, and 2. I doubt they're good practice.
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 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)
I'm trying to test a class and mock one of its methods, but I can't seem to replace the behavior of one of the instance methods with my mocked behavior. My code is organized like so:
--src
----module
------__init__.py
------my_class.py
--tst
----__init__.py
----test_my_class.py
my_class.py contains the following
class MyClass:
def __init__(self):
pass
def do_something(self):
return 'Real Output'
My test file test_my_class.py contains the following.
from unittest.mock import patch
from src.module.my_class import MyClass
def test_my_class():
my_class = MyClass()
assert my_class.do_something() == 'Real Output'
#patch('src.module.my_class.MyClass')
def test_mock_my_class(mock_my_class):
mock_my_class.return_value.do_something.return_value = 'Mocked Output'
my_class = MyClass()
assert my_class.do_something() == 'Mocked Output'
The first test works just fine (no mocking involved so far). The second test, however, gives me the following assertion error. I expect the do_something() method to be mocked and to return "Mocked Output", and for the assert statement to evaluate to true. Where am I going wrong here?
AssertionError: assert <bound method MyClass.do_something of <src.module.my_class.MyClass object at 0x1057133c8>> == 'Mocked Output'
E + where <bound method MyClass.do_something of <src.module.my_class.MyClass object at 0x1057133c8>> = <src.module.my_class.MyClass object at 0x1057133c8>.do_something
PS. I've consulted the following resources without success:
Python mock class instance variable
Mocking Methods on an Instance Variable in Python
Python mock: wrap instance method
How to supply a mock class method for python unit test?
https://medium.com/python-pandemonium/python-mocking-you-are-a-tricksy-beast-6c4a1f8d19b2
That last link looked especially helpful at first, because I'm following one of the examples almost verbatim, but it still doesn't work.
#mock.patch("simple.SimpleClass")
def mock_simple_class(mock_class):
mock_class.return_value.explode.return_value = "BOO!"
inst = simple.SimpleClass()
result = inst.explode()
print(result)
A more appropriate way to solve this is to use patch.object
#patch.object(MyClass, "do_something")
def test_mock_my_class(m):
m.side_effect = ['Mocked Output']
my_class = MyClass()
assert my_class.do_something() == 'Mocked Output'
Personally, patch.object confuses me. After reading this article, I have decided to stick with patch only.
The problem with the patch in the question is that the MyClass patched is not the MyClass used in the test function. Refer to this guide for a more in-depth explanation of where patch should be applied.
The path of the patch should be tst.test_my_class.MyClass, as shown in the code below.
from unittest.mock import patch
import unittest
from src.module.my_class import MyClass
class Test(unittest.TestCase):
def test_my_class(self):
my_class = MyClass()
self.assertTrue(my_class.do_something() == 'Real Output')
#patch('test.test_my_class.MyClass')
def test_mock_my_class(self, mock_my_class):
mock_my_class.return_value.do_something.return_value = 'Mocked Output'
my_class = MyClass()
self.assertTrue(my_class.do_something() == 'Mocked Output')
Run this command to execute it in a terminal.
python3 -m unittest discover
module a.ClassA:
class ClassA():
def __init__(self,callingString):
print callingString
def functionInClassA(self,val):
return val
module b.ClassB:
from a.ClassA import ClassA
class ClassB():
def __init__(self,val):
self.value=val
def functionInsideClassB(self):
obj=ClassA("Calling From Class B")
value=obj.functionInClassA(self.value)
Python unittest class
import unittest
from b.ClassB import ClassB
from mock import patch, Mock, PropertyMock,mock
class Test(unittest.TestCase):
#patch('b.ClassB.ClassA',autospec = True)
def _test_sample(self,classAmock):
dummyMock=Mock()
dummyMock.functionInClassA.return_value="mocking functionInClassA"
classAmock.return_value=dummyMock
obj=ClassB("dummy_val")
obj.functionInsideClassB()
assert dummyMock.functionInClassA.assert_called_once_with("dummy_val")
The assertion fails. Where exactly am I going wrong?
You assigned to return_value twice:
classAmock.return_value=dummyMock
classAmock.return_value=Mock()
That second assignment undoes your work setting up dummyMock entirely; the new Mock instance has no functionInClassA attribute set up.
You don't need to create new mock objects; just use the default return_value attribute value:
class Test(unittest.TestCase):
#patch('b.ClassB.ClassA', autospec=True)
def test_sample(self, classAmock):
instance = classAmock.return_value
instance.functionInClassA.return_value = "mocking functionInClassA"
obj = ClassB("dummy_val")
obj.functionInsideClassB()
instance.functionInClassA.assert_called_once_with("dummy_val")
You do not need to assert the return value of assert_called_once_with() as that is always None (making your extra assert fail, always). Leave the assertion to the assert_called_once_with() method, it'll raise as needed.