Mocking using pytest for class method is not working - python

Following is my Class main.py
class First:
def __init__(self):
ret_val = self.do_verify()
print(ret_val['value'])
if ret_val['value'] is False:
raise Exception
print('works in object')
def do_verify(self):
return {'value': True}
if __name__ == '__main__':
First()
When I run python3 main.py it prints
True \n works in object
Following is my test case test_main.py
from unittest.mock import patch
import pytest
from main import First
def fake_do_verify():
print('works')
return {'value': False}
class TestMain(object):
#patch.object(First, 'do_verify', autospec=True)
def test_main(self, fake_do_verify):
with pytest.raises(Exception):
First()
This is failing my test case. I am not sure what exactly I am missing. It should passes the test case as mocking is making value False.
=================================================== FAILURES ====================================================
______________________________________________ TestMain.test_main _______________________________________________
self = <test_main.TestMain object at 0x7f98190a39e8>, fake_do_verify = <function do_verify at 0x7f98190d1158>
#patch.object(First, 'do_verify', autospec=True)
def test_main(self, fake_do_verify):
with pytest.raises(Exception):
> First()
E Failed: DID NOT RAISE <class 'Exception'>
test_main.py:17: Failed

The fake_do_verify() function that you defined is not used at all; what #patch is doing is create a MagicMock to patch First.do_verify. The mock object returns another mock when called, which returns another mock when indexed, which evaluates as False when compared against False with the is operator.
If you want to patch with your function, you can do it as follows (but change fake_do_verify to take a self argument):
#patch.object(First, 'do_verify', fake_do_verify)
def test_main(self):
...
Alternatively, you could do without defining that function, leave the patch as you originally had it, but set the return value of the mock object:
#patch.object(First, 'do_verify', autospec=True)
def test_main(self, fake_do_verify):
fake_do_verify.return_value = {'value': False}
...

Related

How to Mock a class and its methods which are used by another .py

I am trying to mock a class and its method that is used by another class.
My file structure is:
[1] my_animal.py contains Myclass and method: do_bite()
my_animal.py
class Myclass():
def do_bite(self):
return 1
[2] my_module.py contains jobMain("") which is using the method from my_animal.py
my_module.py
import sys
from my_animal import Myclass
def jobMain(directoryPath):
result = Myclass()
if result.do_bite() is None:
sys.exit(1) # stop here
[3] my_test.py contains the unittest to test jobMain in my_module.py
my_test.py
from my_animal import Myclass
# Try to mock Myclass
#pytest.fixture
def mock_Myclass(monkeypatch):
""" Mock myclass """
monkeypatch.setattr(my_module, "Myclass", MagicMock())
# I tried below code to mock dobite() method, but it was unsuccessfully
# Mock Myclass.dobite to None"""
#pytest.fixture
def mock_dobite(monkeypatch):
def mock_return(*args, **kwargs):
with patch('my_animal',dobite) as p:
instance = p.return_value
instance.dobite = Mock(return_value = None)
monkeypatch.setattr(my_module.Myclass, "do_bite", mock_return )
# My unittest to test dobite() method
def test_dobite(mock_Myclass, mock_dobite):
with pytest.raises(SystemExit) as s_exit:
jobMain("")
assert s_exit.type == SystemExit
assert s_exit.value.code == 1
My question is: How could I mock the return value of the method do_bite() to None or any other expected value?
Your fixture is overly complicated, here is what you can use:
import pytest
from unittest import mock
#pytest.fixture
def mock_dobite():
with mock.patch('my_module.Myclass') as mocked_animal:
mocked_animal.return_value.do_bite.return_value = None
yield
You patch the class as it is imported (see where to patch). Because Myclass is imported like this:
from my_animal import Myclass
you have a local reference to the module, which you have to patch.
mocked_animal is the mocked class. To get the instance of the class, you have to use mocked_animal.return_value. By adding do_bite.return_value, you now can set the return value of the method do_bite.
Now your test will work like this (note that you need only one fixture parameter):
def test_dobite(mock_dobite):
with pytest.raises(SystemExit) as s_exit:
jobMain("")
assert s_exit.type == SystemExit
assert s_exit.value.code == 1

Passing an argument to Patch inside a Pytest fixture, from within a test

This is my fixture which returns a Foo object. I patch Foo's internal config variable, then instantiate the class with a parameter.
#pytest.fixture()
def foo_fix():
patch(Foo.config, "hello"):
def wrapper(parameter):
return Foo(parameter=parameter)
yield wrapper
In my test I do:
def test_foo_1(foo_fix):
foo = foo_fix(parameter=1)
assert foo.go() == "abc"
I would like to vary the Foo.config value from inside the test function. I tried nesting the foo_fix within another function but I couldn't get it to work.
Is there a clean way to do this?
This should work:
#pytest.fixture
def foo_fix():
def wrapper(parameter, config):
patch(Foo.config, "hello"):
return Foo(parameter=parameter)
yield wrapper
def test_foo_1(foo_fix):
foo = foo_fix(parameter=1, config="xyz")
assert foo.go() == "abc"

Mocking a class used in a with statement

I have a class which has an __exit__ and __enter__ function so that I can use it in a with statement, e.g.:
with ClassName() as c:
c.do_something()
I am now trying to write a unit test to test this. Basically, I am trying to test that do_something() has only been called once.
An example (which I called testmocking1):
class temp:
def __init__(self):
pass
def __enter__(self):
pass
def __exit__(self, exc_type, exc_val, exc_tb):
pass
def test_method(self):
return 1
def fun():
with temp() as t:
return t.test_method()
And my test:
import unittest
import test_mocking1
from test_mocking1 import fun
import mock
from mock import patch
class MyTestCase(unittest.TestCase):
#patch('test_mocking1.temp', autospec = True)
def test_fun_enter_called_once(self, mocked_object):
fun()
mocked_object.test_method.assert_called_once()
if __name__ == '__main__':
unittest.main()
So I would expect this to pass, because the test_method has been called exactly once in the function fun(). But the actual result that I get is:
======================================================================
FAIL: test_fun_enter_called_once (__main__.MyTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "<path_to_virtual_env>\lib\site-packages\mock\mock.py", line 1305, in patched
return func(*args, **keywargs)
File "<File_with_test>", line 11, in test_fun_enter_called_once
mocked_object.test_method.assert_called_once()
File "<path_to_virtual_env>\lib\site-
packages\mock\mock.py", line 915, in assert_called_once
raise AssertionError(msg)
AssertionError: Expected 'test_method' to have been called once. Called 0 times.
How do I test whether a function in a class which is created using a with statement has been called (either once or multiple times), and (related) how do I set the results of those calls (using .side_effect or .return_value)?
The with statement takes whatever __enter__ returns to bind to the name in the as <name> part. You bound it to t:
with temp() as t:
t.test_method()
Note that temp() is called, so the with statement starts with temp.return_value. t is not temp.return_value either, it is whatever temp().__enter__() returns, so you need to use the return value for that call:
entered = mocked_object.return_value.__enter__.return_value
entered.test_method.assert_called_once()
Extending on this, if you want to alter what test_method() returns, do so on the return value of mocked_object.return_value.__enter__.return_value.
You can always print out the mock_calls() attribute of your object to see what has happened to it:
>>> from test_mocking1 import fun
>>> from mock import patch
>>> with patch('test_mocking1.temp', autospec = True) as mocked_object:
... fun()
...
>>> print(mocked_object.mock_calls)
[call(),
call().__enter__(),
call().__enter__().test_method(),
call().__exit__(None, None, None)]
>>> mocked_object.return_value.__enter__.return_value.test_method.called
True
>>> mocked_object.return_value.__enter__.return_value.test_method.call_count
1
Note that your actual implementation of temp.__enter__() returns None, so without mocking your fun() function fails with an attribute error.

Patch a method outside python class

I am interested in patching a method which is called by another method in one file. Example - original.py file contains -
def A():
a = 10
b = 5
return a*b;
def B():
c = A()
return c* 10
I want to write unit test for this file , say call it test.py
import mock
import unittest
class TestOriginal(unitest.TestCase):
def test_Original_method(self):
with patch(''):
How can I use patch and mock modules to test original.py. I want A() to always return MagicMock() object instead of an integer.
You simply patch out the A global in the module under test. I'd use the #patch decorator syntax here:
import mock
import unittest
import module_under_test
class TestOriginal(unitest.TestCase):
#patch('module_under_test.A')
def test_Original_method(self, mocked_A):
mocked_A.return_value = 42
result = module_under_test.B()
mocked_A.assert_called_with()
self.assertEqual(result, 420)
This passes in the MagicMock mock object for A() as an extra argument to the test method.
Note that we explicitly named the module here. You could also use patch.object(), just naming the attribute on the module (which are your module globals):
class TestOriginal(unitest.TestCase):
#patch.object(module_under_test, 'A')
def test_Original_method(self, mocked_A):
mocked_A.return_value = 42
result = module_under_test.B()
mocked_A.assert_called_with()
self.assertEqual(result, 420)
You can still use a with statement too, of course:
class TestOriginal(unitest.TestCase):
def test_Original_method(self):
with patch('module_under_test.A') as mocked_A:
mocked_A.return_value = 42
result = module_under_test.B()
mocked_A.assert_called_with()
self.assertEqual(result, 420)

How to mock a method that is not in the scope of a test?

How to mock a method that is not in the scope of a test?
or: How to mock a method that is not called directly?
In this case method baz
I'm using the Mock package from pypi
### tests
# ...
def test_method_a(self):
# how to mock method that is called from bar() ?
obj = foo.bar()
self.assertEqual(obj.get('x'), 12345)
### foo
# ...
def bar():
x = some_module.baz() # <- how to mock baz() ?
return x
Here is an example that should show you how it works:
from mock import patch
def baz():
return 'y'
def bar():
x = baz() # <- how to mock baz() ?
return x
def test():
with patch('__main__.baz') as baz_mock:
baz_mock.return_value = 'blah'
assert bar() == 'blah'
test()
IMHO this example better illustrates the common use case during unit testing.
The important part is the #patch decorator which effectively substitutes the side_effect function (this could also just be a value) for the function being patched. The tricky bit is often getting the full package path to the patched function. Notice how you must use '__main__.func' to reference the patched function.
The first test, test_func checks that the mocked value of the test is the original expected function value ('Hello World!'). The second test, test_mocked_func patches func to actually be the function object new_func, hence returning True. The third test illustrates substituting in values rather than a new function as the side_effect. In fact, since we made the substitution value an iterable (side_effect=['New String 1!', 'New String 2!', 3]), each time it runs, it will return a new value.
Warning: If you try to call your patched function more times than you have specified return values (3 in this case), you will get a StopIteration error since you didn't define enough return values in side_effect.
import unittest
from mock import patch # for Python 2.7
# from unittest.mock import patch # for Python 3.5
def func():
return 'Hello World!'
def newFunc():
return True
class TestFunc(unittest.TestCase):
def test_func(self):
self.assertEqual(func(), 'Hello World!')
#patch('__main__.func', side_effect=newFunc)
def test_mocked_func(self, *patches):
self.assertTrue(func())
#patch('__main__.func', side_effect=['New String 1!', 'New String 2!', 3])
def test_mocked_func_again(self, *patches):
self.assertEqual(func(), 'New String 1!')
self.assertEqual(func(), 'New String 2!')
self.assertEqual(func(), 3)
# func() # This breaks the test because we only specified a list of length 3 in our patch.
if __name__=='__main__':
unittest.main()

Categories