I have some problem with mocking and testing my function/class.
I think the easiest way to explain it will be showing some code:
module.py:
import some_module
class MyClassToTest:
def __init__():
pass
def f(self, a):
if isinstance(a, some_module.someClass):
print("true")
I want to test my module.py:
test_module.py:
import sys
from unittest import mock
sys.modules['some_module'] = mock.MagicMock() # mocking whole module
def test_my_test():
to_test = MyClassToTest()
to_test.f(5)
Of course here I will have an error:
TypeError: isinstance() arg 2 must be a type or tuple of types
because some_module was mocked.
I really need to mock the "some_module" - it's needed for some other tests.
And yes, I probably should unmock the module just for some tests or maybe mock the isinstance when I'm using it in specific function?
Do you have any idea how to process it?
You are in the right way, you've just forgot to set the object spec. Here is a fully functional example based in your question.
some_module.py
class SomeModule(object):
def method(self):
return True
module.py
import some_module
class MyClass:
def __init__(self):
pass
def f(self, a):
if isinstance(a, some_module.SomeModule):
return True
return False
test_module.py
from unittest import mock
from unittest import main, TestCase
import function
class MyClassTestCase(TestCase):
def test_myclass(self):
m = mock.MagicMock(spec=function.some_module.SomeModule)
mc = function.MyClass()
self.assertEqual(mc.f(1), False)
Related
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 have class with few methods. Let's say A
file a.py
class A:
def foo():
...
def bar():
...
during normal runtime (not testing) I use it in different modules, like that.
file functions.py
from a import A
def sample_function():
a_instance = A()
result = a_instance.foo()
return result
But during tests I would like to replace it with different class, let's say MockA.
file mock_a.py
class MockA:
# same methods name, but with different implementation
def foo():
...
def bar():
...
Now I would like to test module with some functionality
tests
from functions import sample_function
def test_sample_function():
assert sample_function() == expected_output
The QUESTION is:
Can I somehow "globally" set alias A = MockA (or do this in other way), so that during tests sample_function use functionality from MockA?
sample_function uses whatever A is bound to in the global namespace of the module functions. You can rebind your own class to that name.
from functions import one_function
class MockA:
...
functions.A = MockA
def test_sample_function():
assert sample_function() == expected_output
This is exactly what unittest.mock.patch is for
How about you use:
tests
from unittest.mock import patch
from mock_a import MockA
from functions import sample_function
#patch('functions.A', new_callable=MockA)
def test_sample_function(mocked_A):
assert sample_function() == expected_output
Try changeing this:
from a import A
into this:
from mock_a import MockA as A
First correct me in comments if I am wrong, but is it a mistake that you wrote one_function in your last code snippet, where you should have written sample_function.
I think your concern is that you don't want to change the functions.py code by replacing a_instance = A() with a_instance = MockA() everywhere in the code. So just make mock_a.py with same class name class A: and same methods name, but different implementations(like you said). All you will have to change in your functions.py code is from mock_a import A instead all all instance of class A() to class MockA(). This way I think your tests should work perfectly.
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)
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.
If I have a class similar to the 1 below and I want to test the various cases for the bar function, how can I accomplish this without mocking the private functions? In other words, how in Python's unittest library could I achieve something similar to this:
def test_bar():
f = Foo()
f.bar(3)
expect(self._is_positive_number).toBeCalled()
foo.py
class Foo():
def bar(self, x):
if type(x) is not int:
print('Please enter a valid integer')
return False
if x > 0:
self._is_positive_number()
elif x == 0:
self._is_zero()
else
self._is_negative()
def _is_positive_number(self):
print('Positive')
return True
def _is_zero(self):
print('Zero')
return True
def _is_negative_number(self):
print('Negative')
return True
As far as I know, there's no way to do this without mocking out the private methods. However, the mock library (available as unittest.mock in the standard library as of 3.3, a separate installation otherwise) makes this relatively painless:
try:
# Python 3.3 or later
import unittest.mock as mock
except ImportError:
# Make sure you install it first
import mock
class TestFoo(unittest.TestCase):
def setUp(self):
self.f = Foo()
def test_bar(self):
with mock.patch.object(self.f, '_is_positive_number') as is_pos:
self.f.bar(3)
self.assertTrue(is_pos.called)
Using mock library is a preferred way to go.
Here's a complete example for all three private methods. You can choose shorter names if you prefer, but I'd better stay explicit. Note that, to be safe, you should assert that not only a desired method was called, but that other private methods weren't called:
from unittest import TestCase
from mock import Mock
class MyTestCase(TestCase):
def setUp(self):
self.instance = Foo()
self.instance._is_positive_number = Mock()
self.instance._is_negative_number = Mock()
self.instance._is_zero = Mock()
def test_positive(self):
self.instance.bar(3)
self.assertTrue(self.instance._is_positive_number.called)
self.assertFalse(self.instance._is_negative_number.called)
self.assertFalse(self.instance._is_zero.called)
def test_negative(self):
self.instance.bar(-3)
self.assertFalse(self.instance._is_positive_number.called)
self.assertTrue(self.instance._is_negative_number.called)
self.assertFalse(self.instance._is_zero.called)
def test_zero(self):
self.instance.bar(0)
self.assertFalse(self.instance._is_positive_number.called)
self.assertFalse(self.instance._is_negative_number.called)
self.assertTrue(self.instance._is_zero.called)