I am trying to mock the below function but I'm not sure how to mock the Connection response:
def get_user_res(user, pass):
res = None
server = Server('my_server')
connnection = Connection(server, user, pass, strategy=SAFE_SYNC, auto_bind=True)
if connection.bind():
connection.search(search_base, search_filter, SUBTREE)
res = connection.response
connection.unbind()
return res
#mock.patch("ldap3.Server")
#mock.patch("ldap3.Connection.response")
def test_get_user_res(mock_connection, mock_server):
mock_connection.return_value = ""
retrived_res = get_user_res("fake_user","fake_password")
expected_res = ""
assert retrived_res == expected_res
The root problem is that you're mocking the wrong things. If you have a file named ldapclient.py that contains your get_user_rest method, like this (note that I've rewritten things a bit to make our lives easier when writing tests):
import ldap3
server = ldap3.Server('my_server')
search_base = 'dc=example, dc=com'
def get_user_res(user, password, search_filter=None):
res = None
connection = ldap3.Connection(
server, user, password,
client_strategy=ldap3.SAFE_SYNC, auto_bind=True)
if connection.bind():
res = connection.search(search_base, search_filter, ldap3.SUBTREE)
connection.unbind()
return res
Then what you need to mock is the ldap3.Connection class. But since your test is in a different module, you'll need to call #mock.patch('ldapclient.ldap3.Connection), assuming that your test is defined like this:
import ldap3
from unittest import mock
import ldapclient
#mock.patch("ldapclient.ldap3.Connection")
def test_get_user_res(mock_connection_class):
mock_connection = mock.Mock()
mock_connection.search.return_value = 'fake_return'
mock_connection_class.return_value = mock_connection
retrived_res = ldapclient.get_user_res("fake_user", "fake_password")
expected_res = "fake_return"
assert retrived_res == expected_res
There are a few things to note here:
As mentioned earlier, because we have import ldapclient, we need to mock ldapclient.ldap3.Connection.
We make the ldap3.Connection class return a new mock.Mock object, since we want to be able to mock methods on the object returned when calling connection = ldap3.Connection(...)
We make the search method return a fake value so that we can ensure it gets called as expected.
Related
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)
my project has a file called config.py which has, among others, the following code:
class Secret(Enum):
DATABASE_A = 'name_of_secret_database_A'
DATABASE_A = 'name_of_secret_database_A'
def secret(self):
if self.value:
return get_secret(self.value)
return {}
def get_secret(secret_name):
session = Session()
client = session.client(
service_name='secretsmanager',
region_name='us-east-1',
)
secret_value = client.get_secret_value(SecretId=secret_name)
return loads(secret_value.get('SecretString', "{}"))
I need to somehow mock get_secret in tests with pytest for all enum calls, for example Secret.DATABASE_A.secret ()
You can use monkeypatch to override the behaviour of get_secret(). I have made the get_secret() method a static method of the Secret class, but you can make it part of any module you want and import it as well. Just make sure you change in in the monkeypatch.setattr() call as well.
import pytest
from enum import Enum
class Secret(Enum):
DATABASE_A = 'name_of_secret_database_A'
DATABASE_B = 'name_of_secret_database_B'
def secret(self):
if self.value:
return Secret.get_secret(self.value)
return {}
#staticmethod
def get_secret(secret_name):
session = Session()
client = session.client(
service_name='secretsmanager',
region_name='us-east-1',
)
secret_value = client.get_secret_value(SecretId=secret_name)
return loads(secret_value.get('SecretString', "{}"))
def test_secret_method(monkeypatch):
def get_secret(secret_name):
return "supersecret"
monkeypatch.setattr(Secret, "get_secret", get_secret)
s = Secret.DATABASE_A
assert s.secret() == "supersecret"
This returns into 1 passed test.
What is happening here is, that I created a function get_secret() in my test_secret_method as well, and then overwrite the Secret.get_secret() with that new method. Now, you can use the Secret class in your test_method and be sure what the 'get_secret()' method will return without actually running the original code.
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)
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
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'])