I have the following test code to test Decorator.
#mock.patch('a.b.c.KafkaProducer')
def test_1(self, mocked):
decorator = Decorator(....)
#decorator()
def test():
return 42
test()
start_time = ???
v = {'type': 'batch', 'start_time': start_time.isoformat()}
mocked.return_value.send.assert_called_once_with(value=v)
However, the test always fail because Decorator calls mocked with dictionary parameter with property of start_time assigned to datetime.now(). Is it a way to compare everything except start_time? Or any other way to test the call?
Two practical approaches:
freeze time using https://pypi.org/project/freezegun/
import datetime
from freezegun import freeze_time
from unittest.mock import patch
import my_library
NOT_NOW = datetime.datetime.now()
#freeze_time("2020-01-01")
#patch("my_library.helper")
def test_foo(_helper):
my_library.under_test(NOT_NOW)
# kinda auto-magic
_helper.assert_called_once_with(datetime.datetime.now())
# or more explicitly
_helper.assert_called_once_with(datetime.datetime(2020, 1, 1))
or, evaluate arguments manually
#patch("my_library.helper", return_value=42)
def test_bar(_helper):
my_library.under_test(NOT_NOW)
assert _helper.call_count == 1
assert _helper.call_args[0][0]
assert _helper.call_args[0][0] != NOT_NOW
Related
I would like to mock some imported class methods and module functions for my unit tests. I tried several ways to define the mocked values but I don't understand why they are not taken into account.
I wrote some tests following the advices in Python Mocking a function from an imported module.
Here is a piece of code representing the application to test:
from services import myModule1
from services.spec1 import importedClass
class myClass(object):
def __init__(self, param1, param2):
self.param1 = param1
self.param2 = param2
self.param3 = 0
self.param4 = 0
self.myMethod()
def myMethod(self):
newVar = importedClass()
self.param3 = newVar.meth1(self.param2)
calcParam = myModule1.methodMod1(self.param1)
self.param4 = calcParam["keyParam3"]
I would like to unit test myMethod and I need to mock importedClass and myModule1.methodMod1().
Here is a new piece of code that I tried for the tests (previous attempts below):
import unittest
from unittest.mock import patch
from my_module import myClass
class test_myClass(unittest.TestCase):
#patch('my_module.importedClass')
#patch('my_module.myModule1')
def test_myMethod(self, mock_mod1, mock_class):
mock_mod1.methodMod1.return_value = {"keyParam3": 5, "keyParam4": 7}
mock_class.meth1.return_value = 2
test_parameters = (0, 0)
test_res = myClass(*test_parameters)
self.assertEqual(test_res.param3, 2)
self.assertEqual(test_res.param4, 5)
if __name__ == '__main__':
unittest.main()
The mocking has no error but the mocked values are not taken into account.
Previous attempts
What I tried for each of them, using 2 different approaches:
import unittest
from unittest.mock import Mock, patch
from my_module import myClass
import services
class test_myClass(unittest.TestCase):
def setUp(self):
services.spec1 = Mock()
services.spec1.importedClass.meth1.return_value = 2
#patch('services.myModule1')
def test_myMethod(self, my_mock):
my_mock.methodMod1.return_value = {"keyParam3": 5, "keyParam4": 7}
test_parameters = (0, 0)
test_res = myClass(*test_parameters)
self.assertEqual(test_res.param3, 2)
self.assertEqual(test_res.param4, 5)
if __name__ == '__main__':
unittest.main()
The result is that the calculated attributes are not updated and still 0 - so test fails.
I also tried with services = Mock() and defined return values for each part, to regroup each mock in setUp method or in a #patch, but nothing worked.
I also tried with my_module.spec1 = Mock(), to make the function global, or even self.spec1 = Mock() to make it very local to the test's context (if I understood correctly the differences, this is something I'm not really sure neither) but nothing worked.
To mock just the method:
class test_myClass(unittest.TestCase):
# #patch('my_module.importedClass') # Change this
#patch('my_module.importedClass.meth1') # to this
#patch('my_module.myModule1')
# def test_myMethod(self, mock_mod1, mock_class): # Change this
def test_myMethod(self, mock_mod1, mock_class_meth1): # to this
mock_mod1.methodMod1.return_value = {"keyParam3": 5, "keyParam4": 7}
# mock_class.meth1.return_value = 2 # Change this
mock_class_meth1.return_value = 2 # to this
test_parameters = (0, 0)
test_res = myClass(*test_parameters)
self.assertEqual(test_res.param3, 2)
self.assertEqual(test_res.param4, 5)
To mock the class and its method:
https://docs.python.org/3/library/unittest.mock-examples.html#mocking-classes
class test_myClass(unittest.TestCase):
#patch('my_module.importedClass')
#patch('my_module.myModule1')
def test_myMethod2(self, mock_mod1, mock_class):
mock_mod1.methodMod1.return_value = {"keyParam3": 5, "keyParam4": 7}
# mock_class.meth1.return_value = 2 # Change this
mock_class_instance = mock_class.return_value # to these
mock_class_instance.meth1.return_value = 2 # two lines
test_parameters = (0, 0)
test_res = myClass(*test_parameters)
self.assertEqual(test_res.param3, 2)
self.assertEqual(test_res.param4, 5)
Let's say I have the following python files:
# source.py
def get_one():
return 1
# file1.py
from source import get_one
def func1():
return get_one()
# file2.py
from source import get_one
def func2():
return get_one()
# script.py
from file1 import func1
from file2 import func2
def main(a, b):
count = 0
for _ in range(a):
count += func1()
for _ in range(b):
count += func2()
return count
I know I can mock out get_one() in main.py using the following setup:
def test_mock():
with (
patch("file1.get_one") as mock1,
patch("file2.get_one") as mock2,
):
main(2, 3)
assert mock1.call_count + mock2.call_count == 5
However, this gets increasingly verbose and hard to read if get_one() needs to be mocked out in many files. I would love to be able to mock out all of its locations in a single mock variable. Something like:
# this test fails, I'm just showing what this ideally would look like
def test_mock():
with patch("file1.get_one", "file2.get_one") as mock:
main(2, 3)
assert mock.call_count == 5
Is there anyway to do this or do I need to use multiple mocks?
Note, I know I can't mock where the function is defined, e.g. patch("source.get_one").
patch accepts new as the object to patch the target with:
def test_mock():
with (
patch("file1.get_one") as mock,
patch("file2.get_one", new=mock),
):
main(2, 3)
assert mock.call_count == 5, mock.call_count
You can write a helper context manager:
import contextlib
from unittest.mock import DEFAULT, patch
#contextlib.contextmanager
def patch_same(target, *targets, new=DEFAULT):
with patch(target, new=new) as mock:
if targets:
with patch_same(*targets, new=mock):
yield mock
else:
yield mock
Usage:
def test_mock():
with patch_same("file1.get_one", "file2.get_one") as mock:
main(2, 3)
assert mock.call_count == 5
A possible workaround: add a level of indirection in source.py so that get_one has a dependency that you can patch:
def get_one():
return _get_one()
def _get_one():
return 1
and now you can do this in your test:
from script import main
from unittest.mock import patch
def test_mock():
with patch("source._get_one") as mock1:
main(2, 3)
assert mock1.call_count == 5
As the title says. I want to mock an imported function from a module. In this case the module is datetime and I want to mock datetime.datetime.now. I included what I've done without success.
# main.py:
import datetime
# cannot modify
def call_me(func):
return func()
class A:
variable = call_me(datetime.datetime.now)
# conftest.py:
import pytest
import datetime
FAKE_TIME = datetime.datetime.fromisoformat("2020-03-19T03:30:00")
#pytest.fixture(autouse=True)
def patch_datetime_now(monkeypatch):
class mydatetime(datetime.datetime):
#classmethod
def now(cls):
return FAKE_TIME
monkeypatch.setattr('datetime.datetime', mydatetime)
# test_main.py:
import datetime
from main import A
def test_main():
assert A.variable == datetime.datetime.now()
❯ pytest
------------------------------------------------------------------------------------
def test_main():
> assert A.variable == datetime.datetime.now()
E AssertionError: assert datetime.datetime(2020, 3, 19, 21, 32, 39, 861956) == datetime.datetime(2020, 3, 19, 3, 30)
I searched for a workaround for this and only found this question How to monkeypatch python's datetime.datetime.now with py.test?. The code included is just a minimal example of what I need. Basically I have an app who uses sqlalchemy.orm for saving data and a specific model have a default value for the date equal to datetime.datetime.now and I need to modify the date of the default value to be able to test it. The models are defined in other file.
Thank you for your help.
I'm new to mock and really strugling with it. Mostly in documentation and most of SO pages it shows how to get the mock result_value, but I want to check if the values I'm getting from methods are correct, not the result_value's. Here's the example:
#!/usr/bin/env python
class Example:
def one(self):
return 1
def two(self, one):
print(one + 1) # basically any void method, ex: result = one + 1 and check result value if correct
def main(self):
self.two(self.one())
if __name__ == '__main__':
e = Example()
e.main()
Test:
#!/usr/bin/env python
import unittest
import example
from mock import patch
class Example(unittest.TestCase):
def test_one(self):
self.assertEqual(1, et.one())
def test_two(self):
with patch('example.Example.two'):
self.assertEqual(2, et.two(et.one())) # ..the part I'm stuck
# whick ofc throws AssertionError: 2 != <MagicMock name='two()' id='blablabla'>
def test_main(self):
# unknown part..
if __name__ == '__main__':
et = example.Example()
unittest.main()
How to achieve void method check with unittest?
UPDATE:
so the print I figured out with chepner's help:
def test_twoi3(self):
mock_print = MagicMock()
with patch('sys.stdout', mock_print):
print(2)
expected = call.write('2')
self.assertEqual(mock_print.mock_calls[0], expected)
and for main I'm not quite sure if it is a good solution...:
def test_main(self):
with patch ('example.Example.main') as m:
et.main(et.two(1))
m.assert_called_with(et.two(1))
but I want to check not by passing the methods and values, but if main calls other two methods. How to achieve this?
You don't need to mock anything (directly, anyway); you want to capture standard output and verify that it is what you expect.
from contextlib import redirect_stdout
from io import StringIO
def test_two(self):
stdout = StringIO()
with redirect_stdout(stdout):
et.two(et.one())
self.assertEqual(stdout.getvalue(), "2\n")
Or, you can mock the print and check that it is called with the expected arguments.
def test_two(self):
with patch('__builtin__.print') as p:
et.two(et.one())
p.assert_called_with(2)
I have figured out how to test if main methods have been called. Test looks like this:
#!/usr/bin/env python
import unittest
import example
from mock import patch, MagicMock, call
class Example(unittest.TestCase):
def setUp(self):
self.et = example.Example()
def test_one(self):
self.assertEqual(1, self.et.one())
def test_two(self):
mock_print = MagicMock()
with patch('sys.stdout', mock_print):
print(2)
expected = call.write('2')
self.assertEqual(mock_print.mock_calls[0], expected)
def test_main(self):
self.et.two = MagicMock(side_effect=self.et.two)
self.et.one = MagicMock(side_effect=self.et.one)
self.et.main()
self.et.one.assert_called()
self.et.two.assert_called()
self.et.one.__str__ = self.et.one
self.assertEqual(1, int(self.et.one))
if __name__ == '__main__':
unittest.main()
Upon mocking methods that are in main (all of them) and calling main methods one and two have been successfully called. For one you can return value by using __str__
I try to add unit test in python in function that save stats in a file
Here is the function for saving
def save_file_if_necessary(file_path, content, current_time, mode="w", delta_time=60, force=False):
if file_path not in file_save or current_time - file_save[file_path] >= delta_time or force:
with codecs.open(file_path, mode, encoding="utf-8") as written_file:
written_file.write(content)
file_save[file_path] = time.time()
print "yes"
return True
else:
print "not necessary"
return False
I make a call of this function like that
def test_function():
bot_url_dic = {"seven1": 10,
"seven2": 20
}
save_file_if_necessary(os.path.join("./", "recipients.bots"),json.dumps(bot_url_dic, ensure_ascii=False, indent=4), time.time())
And i made some unittest with mock to test if the function is called
from test import save_file_if_necessary, test_function
def test_call_save_file_if_necessary(self):
"""test function to test add in list."""
ip_dic = ["seven1", "seven2", "seven3"]
save_file_if_necessary = Mock()
test_function()
self.assertTrue(save_file_if_necessary.called)
But the problem is Mock is always return False but the function is called at least one time.
self.assertTrue(save_file_if_necessary.called)
AssertionError: False is not true
(python version 2.7.6)
All you've done is create a new Mock object, coincidentally called "save_file_if_necessary". You haven't done anything to replace the actual function with your mock.
You need to use the patch functionality to actually do that:
#mock.patch('my_test_module.save_file_if_necessary')
def test_call_save_file_if_necessary(self, mock_function):
ip_dic = ["seven1", "seven2", "seven3"]
test_function()
self.assertTrue(mock_file.called)
You need to import the module where the function is defined and assign a Mock to your function:
import test
def test_call_save_file_if_necessary(self):
"""test function to test add in list."""
ip_dic = ["seven1", "seven2", "seven3"]
test.save_file_if_necessary = Mock()
test.test_function()
self.assertTrue(test.save_file_if_necessary.called)
Or, use the patching function instead.