I'm pretty new to patching and I've run into a something I don't know how to patch. Basically, in the file I want to test, there is the method difficult_method(). It looks a little like this:
from import_location import User
def difficult_method():
ids = list_of_ids
for id in list_of_ids:
try:
user = User.query.filter(User.id == user_id).all()[0]
except:
continue
#do lots of stuff
The code I want to mock is User.query.filter(User.id == user_id).all() and as far as I am concerned it can return a static list. How would I replace that line in code that looks something like this:
from mock import patch
#patch(#what would go here?)
def test_difficult_method():
from file_to_test import difficult_method
assert difficult_method() returns ...
I figured it out! The key was to create a MockUser class, like so:
user = #creating a user
class MockFilter(object):
def all(self):
return [user]
class MockQuery(object):
def filter(self, match):
return MockFilter()
class MockUser(object):
query = MockQuery()
id = '2'
Then I patched it in like so:
from mock import patch
#patch('import_location.User', MockUser)
def test_difficult_method():
from file_to_test import difficult_method
assert difficult_method() returns ...
Related
# test.py
from freezegun import freeze_time
from actual import DummyClass
class DummyClassMock(DummyClass):
def some_function(self):
# does something
class TestClass():
def setUp(self):
self.dummy = DummyClassMock()
#freeze_time('2021-01-01')
def test_dummy_function(self):
self.assertTrue((self.dummy.dummy_function - datetime.utcnow()).total_seconds() >= 1)
# actual.py
from datetime import datetime, timedelta
class DummyClass():
def dummy_function(self):
return datetime.utcnow() + timedelta(5)
My code goes along the above structure. With this, if I am executing the test_dummy_function individually, dummy_function is returning 2021-01-01 and test case is a pass. However, when I running this along with all the other test cases in the project, it is failing. Content is not dependent either.
Not particularly a good solution but, the workaround I used was to define a function that would just return datetime.utcnow() and mock it. My test case will assign the date used in freeze_time as return value. It looks something like,
#mock.patch(actual.DummyClass.now_function)
#freeze_time('2021-01-01')
def test_dummy_function(self, mock_dt):
now = datetime.utcnow()
mock_dt.return_value = now
self.assertTrue((self.dummy.dummy_function - now).total_seconds() >= 1)
# actual.py
Class DummyClass():
def now_function():
return datetime.utcnow()
def dummy_function():
return now_function()+timedelta(days=5)
I have code that looks like the following:
#patch.object(Path, "__init__", return_value=None)
#patch.object(Path, "exists", return_value=True)
#patch.object(Path, "read_text", return_value="FAKEFIELD: 'FAKEVALUE'")
def test_load_param_from_file_exists(self, *mock_path):
expected_dict = YAML.safe_load("FAKEFIELD: 'FAKEVALUE'")
return_dict = load_parameters("input", None)
self.assertTrue(return_dict["FAKEFIELD"] == expected_dict["FAKEFIELD"])
and deep in the code of load_parameters, the code looks like this:
file_path = Path(parameters_file_path)
if file_path.exists():
file_contents = file_path.read_text()
return YAML.safe_load(file_contents)
Right now, I have to break it up into two tests, because I cannot seem to get a single mock object that allows me to switch between "file exists" and "file doesn't". Ideally, I'd be able to do a single test like this:
#patch.object(Path, "__init__", return_value=None)
#patch.object(Path, "exists", return_value=False)
#patch.object(Path, "read_text", return_value="FAKEFIELD: 'FAKEVALUE'")
def test_load_param_from_file(self, mock_path, *mock_path_other):
with self.assertRaises(ValueError):
load_parameters("input", False)
mock_path.read_text.return_value = "FAKEFIELD: 'FAKEVALUE'"
expected_dict = YAML.safe_load("FAKEFIELD: 'FAKEVALUE'")
return_dict = load_parameters("input", None)
self.assertTrue(return_dict["FAKEFIELD"] == expected_dict["FAKEFIELD"])
To be clear, the above doesn't work because each of those patched objects get instantiated differently, and when the Path object in the load_parameters method gets called, exists is mocked correctly, but read_text returns no value.
What am I doing wrong? Is there a way to patch multiple methods on a single object or class?
I think you are making this more complicated than it needs to be:
def test_load_param_from_file_exists(self):
# Adjust the name as necessary
mock_path = Mock()
mock_path.exists.return_value = True
mock_path.read_text.return_value = '{"FAKEFIELD": "FAKEVALUE"}'
with patch("Path", return_value=mock_path):
return_dict = load_parameters("input", None)
self.assertTrue(return_dict["FAKEFIELD"] == 'FAKEVALUE')
Configure a Mock to behave like you want file_path to behave, then patch Path to return that object when it is called.
(I removed the code involving the environment variable, since it wasn't obvious the value matters when you patch Path.)
I have a python module mymodule.py:
def auth():
'''Authorize and generate a JSON file'''
return j
j = auth()
def get_value(key):
'''Takes the key and return value from JSON'''
value = j[key]
return value
I have a program where I use this module myprogram.py:
import mymodule
keys = [1,2,3,4,5]
def simple_program(keys):
# mymodule.auth() should I place it here?
for key in keys:
value = mymodule.get_value(key)
return value
So the goal is to call mymodule.auth() once, every time I run simple_program to refresh the JSON file. I don't know how to achieve this. Because myprogram.py is also a module and I call simple_program() from another .py file. So where do I place mymodule.auth()? Is it ok to place mymodule.auth() inside simple_program?
The instant you import mymodule the code below runs
j = auth()
which is why when you call mymodule.get_value() it works. This causes J to be a singleton in the global space. Everytime you import this, auth() will run again. This could be bad.
What you could do is this:
def auth():
'''Authorize and generate a JSON file'''
return j
j = None
def get_value(key):
global j
'''Takes the key and return value from JSON'''
if not j:
j = auth()
value = j[key]
return value
Now you just need to run get_value() and everything should work fine. No need to execute auth() again.
Your exact use case is a little vague (e.g. simple_program is not the main program but smth like a subroutine? and it is called several times from another py file?), but it seems to me like you should get familiar with classes. I would suggest to implement auth() as a class, e.g. like this:
class MyJson(object):
def __init__(self):
self._json = ... # do authorization and generation here and save the result as member
def get_value(self, key):
value = self._json[key]
return value
Now import and create an object of that class wherever you need it for the first time
from mymodule import MyJson
# ...
my_json = MyJson()
If you only need it to be initialized once, do that in your main program and pass the my_json object as parameter to simple_program (which should possibly also be a class). And then use it like
value = my_json.get_value(key)
I have a view that is decorated like this:
#check_has_permission('is_manager')
def view():
pass
and the decorator looks something like this:
def check_has_permission(group=None):
def can_user_access(user):
if user.is_authenticated():
if group == 'is_staff':
return user.is_staff()
elif group == 'is_admin':
return (
user.is_admin() or
user.is_staff()
)
elif group == 'is_manager':
return (
user.is_manager() or
user.is_admin() or
user.is_staff()
)
return False
return user_passes_test(can_user_access, login_url=login_url)
How do I write a unit test to confirm the view is decorated with check_has_permission with a group of 'is_manager'?
I'm hoping I can write a set of tests for the decorator itself, then I only have to confirm I'm calling the right group for each view it decorates.
You need to add a seam where you can test.
If your decorator looks like this:
def is_user_in_group(group):
if user.is_authenticated():
# ... snip - checks on groups and user ...
return False
def check_has_permission(group=None):
def can_user_access(user):
return is_user_in_group(user, group)
return user_passes_test(can_user_access, login_url=login_url)
Then your test can mock both is_user_in_group and user_passes_test, call the decorated function, test that is_user_in_group gets called with the correct group, and test that user_passes_test gets called with the return value from your mock of is_user_in_group.
I am fairly new to Python and having a hard time wrapping my head around how to mock patch API calls in unit testing.
FYI, I am using Python 2.7 and using nosetest for my unit testing needs.
I have the following module (myRedis.py) which I want to unit test:
import logging
import redis
redispool = None
class myRedis(object):
def __init__(self, redisHost, redisPort, redisDBNum):
if not redisPort.isdigit():
raise TypeError('Exception: Expected int for redisPort')
if not redisDBNum.isdigit():
raise TypeError('Exception: Expected int for redisDBNum')
self._redis_instance = None
self._redishost = redisHost
self._redisport = redisPort
self._redisdb = redisDBNum
global redispool
redispool = redis.ConnectionPool(host=self._redishost,
port=self._redisport,
db=self._redisdb)
def redis_connect(self):
LOGGER.info('Connecting Redis client to %s:%s:%s', self._redishost,
self._redisport, self._redisdb)
self._redis_instance = redis.StrictRedis(connection_pool=redispool)
def write_redis(self, key, value):
retval = self._redis_instance.set(key, value)
LOGGER.info('Writing data to redis (%s, %s). Retval=%s', key, value, retval)
return retval
def read_redis(self, key):
retval = self._redis_instance.get(key)
LOGGER.info('Reading data from redis: key=%s. Retval=%s', key, retval)
return retval
As far as unit testing goes, I have the following so far.
from nose.tools import *
import mock
from myRedis import myRedis
def setup():
pass
def teardown():
pass
#mock.patch('redis.StrictRedis')
def test_mock_redis_StrictRedis(mock_conn_pool):
mock_conn_pool.return_value = True
assert(True)
def test_myRedis():
assert_raises(TypeError, myRedis,
'test', '1234', 'a11')
assert_raises(TypeError, myRedis,
'test', 'a1234', '11')
myRedisObj = myRedis('localhost', '8888', '11')
assert_equal(myRedisObj._redishost, 'localhost')
assert_equal(myRedisObj._redisport, '8888')
assert_equal(myRedisObj._redisdb, '11')
myRedisObj.redis_connect()
#oclRedis.read_redis('test')
#oclRedis.write_redis('test', 'test')
I am able to patch the redis.StrictRedis() call without any problems. But how do I patch the redis' get() and set() calls since they are invoked on an object (_redis_instance in myRedis.py). I tried a few different versions of #mock.patch.object, but that didn't work for me. Looking for some guidance.
Thanks in advance.
What you should patch are not the actual calls from your object but actually the object itself that those calls are invoking.
In your code it would something along the lines of:
from nose.tools import *
import mock
import unittest
from red import myRedis
def setup():
pass
def teardown():
pass
#mock.patch('redis.StrictRedis')
def test_mock_redis_StrictRedis(mock_conn_pool):
mock_conn_pool.return_value = True
assert(True)
def test_myRedis_wrong_args():
assert_raises(TypeError, myRedis,
'test', '1234', 'a11')
assert_raises(TypeError, myRedis,
'test', 'a1234', '11')
def test_myRedis_ok():
myRedisObj = myRedis('localhost', '8888', '11')
assert_equal(myRedisObj._redishost, 'localhost')
assert_equal(myRedisObj._redisport, '8888')
assert_equal(myRedisObj._redisdb, '11')
#mock.patch('redis.StrictRedis.set')
def test_myRedis_write(mock_strict_redis_set):
mock_strict_redis_set.return_value = {}
myRedisObj = myRedis('localhost', '8888', '11')
redis_connect = myRedisObj.redis_connect()
connect = myRedisObj.write_redis('1', '2')
assert connect == {}
As you can see I modified your tests to test one thing at a time. This is something that you generally want to do to avoid side-effects and guarantee test isolation.
Consider taking a look at the docs: https://docs.python.org/dev/library/unittest.mock.html
Finally indenting is key in Python, consider proper indenting in your code snippets going forward