mock queryset in Django unittest - python

I have sample code and test:
def outer():
inner_response = inner(param1)
def inner(something):
queryset_response = something.object.filter(foo="bar",
foo1="bar1") #this should get a reponse when testing.
response_list = []
for count, queryset_res in enumerate(queryset_response):
response_list.append(queryset_response[count].data)
#get response for data in this line.
return response_list
I wanna test this situation using mock and probably return list of queryset using mock if possible.
def setup():
something = mock.Mock()
def test_outer():
# what should be done to the below line work so that
# response_list.append gets some value.
something.objects.filter()[0].data = "some string"
# Also is it possible to return queryset as like shown below.
something.objects.filter().return_value = <queryset> # list of objects in queryset.

i used a mock and returned namedtuple to make it behave like queryset and use (.) dot to access the data set. I could make a class and used it same way.
def test_func():
something = mock.Mock()
key = collections.namedtuple('key', 'data')
response = key('some_string')
something.objects.filter.return_value = [response]
this is kinda mocking django as gloo said, my ex-engineers decided to opt in that way.

You shouldn't be mocking the result of a filter as that would be like unit testing django itself. Instead, you should be testing your own functions that will call Model.object.filter. You can create the objects you will be working with in the setup of your unit test, and assert that when you call your function, the expected result is the same as those objects. For example:
def my_own_function(foo, foo1):
queryset_response = Something.objects.filter(foo=foo, foo1=foo1)
store = queryset_response[0].data
return store
and in your unit test:
def test_my_own_function():
data = "mydata"
sample_obj = Something.objects.create(foo="bar", foo1="bar1", data=data)
result = my_own_function("bar", "bar1")
self.assertEqual(result, data)

Related

Mock and patch class method and give its return value to another function

Here are my snippets
module1.py
class Client:
def __init__(self):
self.api_client = APIClient()
def get_resources(self):
#this method gets some data
#end returns list with dictionaries
return [{k1:v1},{k2:v2} ...]
module2
config = {}
def add_config(resource):
#process the data pass by resource
config[resource[k1]] = data
def instantiate_config():
for item in Client().get_resources()
add_config(item)
So I want to test this instantiate_config with pytest. Here is my try:
#patch('module1.Client.get_resources')
def test_instantiate_config(self, client_mock):
dummy_data = {some_dummy_data}
#it is a copy of the list, returned form Client().get_resources()
client_mock.get_resources.returned_values = dummy_data
instantiate_config()
assert 'key1' in config #config is the same config from module2
But this gives empty config dict. I don't know is that possible - to mock Client().get_resources() to give it same value and that value to be passed automatically as argument to add_config_func. If it it not what is the best way to test instantiate_config function. Not sure is in clear or not, cuz it is a little bit long story
Your mock is already representing the method get_resources, and you have a typo in "returned_values". Change to : client_mock.return_value = dummy_data

Mocking returns Mock Object instead of return value

I mocked the following function os.getenv, but instead of getting the return_value I specified, the Mock Object itself is returned. What am I doing wrong here?
#staticmethod
def setup_api_key():
load_dotenv() # loading apikey and secret into PATH Variables
api = os.getenv('APIKEY')
secret = os.getenv('SECRET')
return api, secret
The test looks like this:
def test_setup_api_key(self):
with patch('os.getenv') as mocked_getenv:
mocked_getenv = Mock()
mocked_getenv.return_value = '2222'
result = Configuration.setup_api_key()
self.assertEqual(('2222', '3333'), result)
When you use patch in the context-manager fashion, the object you get (mocked_getenv) is already a Mock object so you don't have to recreate it:
def test_setup_api_key(self):
with patch('os.getenv') as mocked_getenv:
mocked_getenv.return_value = '2222'
result = Configuration.setup_api_key()
self.assertEqual(('2222', '3333'), result)
You can make this code a bit simpler by providing the return value directly when creating the context manager:
def test_setup_api_key(self):
with patch('os.getenv', return_value='2222') as mocked_getenv:
result = Configuration.setup_api_key()
self.assertEqual(('2222', '3333'), result)

Mock nested method in Python

Wracking my brain on this. I want to mock generator methods self.api.redditor(username).comments.new(limit=num) and self.api.redditor(username).submissions.new(limit=num) below, in which self.api is assigned to a class instance, as in self.api = PrawReddit()
I'm trying to test the size of the result: self.assertEqual(len(result), 5)
So far, I tried MockPraw.return_value.redditor.return_value.comments.return_value.new.return_value.__iter__.return_value = iter(['c' * 10]) but the test fails with AssertionError: 0 != 5
Any tips much appreciated.
def get_comments_submissions(self, username, num=5):
"""Return max `num` of comments and submissions by `username`."""
coms = [
dict(
title=comment.link_title,
text=comment.body_html,
subreddit=comment.subreddit_name_prefixed,
url=comment.link_url,
created=datetime.fromtimestamp(comment.created_utc, pytz.utc),
)
for comment in self.api.redditor(username).comments.new(limit=num)
]
subs = [
dict(
title=submission.title,
text=submission.selftext_html,
subreddit=submission.subreddit_name_prefixed,
url=submission.url,
created=datetime.fromtimestamp(submission.created_utc, pytz.utc),
)
for submission in self.api.redditor(username).submissions.new(limit=num)
]
return coms + subs if len(coms + subs) < num else (coms + subs)[:num]
To mock a generator (unless you are using specific generator features) you can use an iterator as a stand-in eg
import unittest.mock as mock
generator_mock = Mock(return_value=iter(("foo", "bar")))
When you have nested structures like in your example this gets a little more complex, attribute access is automatically handled but return_value from a function must be defined. From your example:
# API mock
mock_api = Mock()
mock_api.redditor.return_value = mock_subs = Mock()
# Submissions mock
mock_subs.new.return_value = iter(("foo", "bar"))
This can then be called and asserted
for item in mock_api.api.redditor("user").submissions.new(limit=5):
print(item)
mock_api.redditor.assert_called_with("user")
mock_subs.new.assert_called_with(limit=5)
As the API is a member of the same class, this is going to have to be monkey patched eg:
target = Praw()
target.api = mock_api()
target.get_comments_submissions("user")
mock_api.redditor.assert_called_with("user")
mock_subs.new.assert_called_with(limit=5)
Note that the iterator in return value is a single instance and a second call to get the iterator will return the same instance.
Writting like you use pytest-mock and everything happens in mymodule (you imported the class at the top of the module like from xy import PrawReddit):
mocker.patch("datetime.fromtimestamp")
mocked_comment = mocker.MagicMock()
mocked_submission = mocker.MagicMock()
mocked = mocker.patch("mymodule.PrawReddit")
mocked.return_value.redditor.return_value.comments.new.return_value = [mocker.MagicMock(), mocked_comment]
mocked.return_value.redditor.return_value.submisions.new.return_value = [mocker.MagicMock(), mocked_submission]
returned = instance.get_comments_submissions("foo", num=2)
assert mocked.return_value.redditor.call_count = 2
mocked.return_value.assert_called_with("foo")
assert returned[-1]["link_title"] == mocked_comment.link_title
Another test call with the same intro:
# ...
returned = instance.get_comments_submissions("foo")
assert mocked.return_value.redditor.call_count = 2
mocked.return_value.assert_called_with("foo")
assert returned[1]["link_title"] == mocked_comment.link_title
assert returned[-1]["title"] == mocked_submission.title

How to mock a function with specific parameter and return value that calls another function in python unit test?

i am new to python unit testing. Want to mock a function that calls other functions.
Here is my function that i want to mock
def has_groups(self, group_names):
auth_user_id = AuthUser.get_by_email(self.userEmail).id
auth_user_groups = AuthUserGroups.get_group_by_user_id(auth_user_id)
for auth_user_group in auth_user_groups:
if auth_user_group.group.name in group_names:
return True
return False
has_groups should return True only when it get's 'Admin' as parameter.
Here is my test
def my_test(self):
uid = self.auth_user.get_by_email = Mock(return_value=73)
groups = AuthUserGroups.get_group_by_user_id = Mock(uid, return_value='Admin')
self.auth_user.has_groups = Mock(groups, return_value=True)
but it's not working fine. I will appreciate if anyone help me
Can i use patch decorator for this and how?
As I understand your has_groups function is method. I think it's better to mock whole class or independent function. On this situation you could mock AuthUserGroups, method return value and patch module with has_groups method implementation. So you'll have test like this:
from unittest import mock
def my_test(self):
group = mock.MagicMock()
group.group.name = 'Admin'
fake_auth_user_groups = mock.MagicMock()
fake_auth_user_groups.get_group_by_user_id.return_value = [group]
with mock.patch('your_module.AuthUserGroups', fake_auth_user_groups):
self.auth_user.has_groups(['Admin'])

Unit testing complex `__init__` method

i have this piece of code which is a class __init__ method and takes two arguments.
def __init__(self, port_type, request):
self.log = Log(__name__, True)
self.request = request
self.sitecode = port_type.sitecode.upper()
self.browser_default_lang = self.request.META['HTTP_ACCEPT_LANGUAGE'].split(',')[0]
self.active_lang = self.request.session.get('lang', self.browser_default_lang.lower())
self.static_folder = 'static_%s_%s' % (self.sitecode, self.SITE_TEMPLATES_RESOURCES_SUFFIX)
self.template_path = os.path.join(settings.MEDIA_ROOT, self.static_folder)
self.template_path_port_type = '%s_%s' % (self.template_path, self.port_type.hash)
self.site_media_path = os.path.join(settings.MEDIA_URL, self.static_folder)
self.site_port_type_media_path = '%s_%s' % (self.site_media_path, self.port_type.hash)
self.site_config = SiteConfig.objects.get(sitecode=self.sitecode)
self.site = UmSite.objects.get(code=self.port_type.sitecode)
self.context = {}
I find it difficult to write unit tests for this. I should really write the tests first but somehow I ended up with this and have to refactor and am writing them now.
Use a mock request that gives you deterministic data, without connecting to the internet. For example, make a request object that returns some fixed string for request.META, and a session object that returns a fixed string for session.get(...), etc. Then assert that self.active_lang and other properties that need to be set have the right values.

Categories

Resources