I am in the process of learning unit testing, however I am struggling to understand how to mock functions for unit testing. I have reviewed many how-to's and examples but the concept is not transferring enough for me to use it on my code. I am hoping getting this to work on a actual code example I have will help.
In this case I am trying to mock isTokenValid.
Here is example code of what I want to mock.
<in library file>
import xmlrpc.client as xmlrpclib
class Library(object):
def function:
#...
AuthURL = 'https://example.com/xmlrpc/Auth'
auth_server = xmlrpclib.ServerProxy(AuthURL)
socket.setdefaulttimeout(20)
try:
if pull == 0:
valid = auth_server.isTokenValid(token)
#...
in my unit test file I have
import library
class Tester(unittest.TestCase):
#patch('library.xmlrpclib.ServerProxy')
def test_xmlrpclib(self, fake_xmlrpclib):
assert 'something'
How would I mock the code listed in 'function'? Token can be any number as a string and valid would be a int(1)
First of all, you can and should mock xmlrpc.client.ServerProxy; your library imports xmlrpc.client as a new name, but it is still the same module object so both xmlrpclib.ServerProxy in your library and xmlrpc.client.ServerProxy lead to the same object.
Next, look at how the object is used, and look for calls, the (..) syntax. Your library uses the server proxy like this:
# a call to create an instance
auth_server = xmlrpclib.ServerProxy(AuthURL)
# on the instance, a call to another method
valid = auth_server.isTokenValid(token)
So there is a chain here, where the mock is called, and the return value is then used to find another attribute that is also called. When mocking, you need to look for that same chain; use the Mock.return_value attribute for this. By default a new mock instance is returned when you call a mock, but you can also set test values.
So to test your code, you'd want to influence what auth_server.isTokenValid(token) returns, and test if your code works correctly. You may also want to assert that the correct URL is passed to the ServerProxy instance.
Create separate tests for different outcomes. Perhaps the token is valid in one case, not valid in another, and you'd want to test both cases:
class Tester(unittest.TestCase):
#patch('xmlrpc.client.ServerProxy')
def test_valid_token(self, mock_serverproxy):
# the ServerProxy(AuthURL) return value
mock_auth_server = mock_serverproxy.return_value
# configure a response for a valid token
mock_auth_server.isTokenValid.return_value = 1
# now run your library code
return_value = library.Library().function()
# and make test assertions
# about the server proxy
mock_serverproxy.assert_called_with('some_url')
# and about the auth_server.isTokenValid call
mock_auth_server.isTokenValid.assert_called_once()
# and if the result of the function is expected
self.assertEqual(return_value, 'expected return value')
#patch('xmlrpc.client.ServerProxy')
def test_invalid_token(self, mock_serverproxy):
# the ServerProxy(AuthURL) return value
mock_auth_server = mock_serverproxy.return_value
# configure a response; now testing for an invalid token instead
mock_auth_server.isTokenValid.return_value = 0
# now run your library code
return_value = library.Library().function()
# and make test assertions
# about the server proxy
mock_serverproxy.assert_called_with('some_url')
# and about the auth_server.isTokenValid call
mock_auth_server.isTokenValid.assert_called_once()
# and if the result of the function is expected
self.assertEqual(return_value, 'expected return value')
There are many mock attributes to use, and you can change your patch decorator usage a little as follows:
class Tester(unittest.TestCase):
def test_xmlrpclib(self):
with patch('library.xmlrpclib.ServerProxy.isTokenValid') as isTokenValid:
self.assertEqual(isTokenValid.call_count, 0)
# your test code calling xmlrpclib
self.assertEqual(isTokenValid.call_count, 1)
token = isTokenValid.call_args[0] # assume this token is valid
self.assertEqual(isTokenValid.return_value, 1)
You can adjust the code above to satisfy your requirements.
Related
I am trying to test with my code by mocking the PyGithub library.
I want to create a repository for an organization. So first I need to get it and on the "Organization" returned object, I need to make another call.
It fails when trying to assert that my second method was called.
I am very new to python and I am guessing that there is a missing connection between the mocks but I cannot figure out what.
class GithubService:
def __init__(self, token: str) -> None:
self.__github__ = Github(token)
self.__token__ = token
def create_repo_extra(self, repo_name, description, organization_name, team_name):
try:
organization = self.__github__.get_organization(organization_name)
repo = organization.create_repo(name=repo_name,
description=description,
private=True,
has_issues=False,
has_wiki=False,
has_projects=False,
allow_merge_commit=False)
# do other things with the returned repo.....
return True
except GithubException as ex:
print(ex.data)
return False
Here is the test:
import unittest
from unittest.mock import patch, MagicMock, ANY
from github.Organization import Organization
from github.Repository import Repository
from src.github_service import GithubService
class TestGithubService(unittest.TestCase):
#patch('github.Organization.Organization.create_repo',
side_effect=MagicMock(return_value=Repository(ANY, {}, {}, True)))
#patch('github.MainClass.Github.get_organization',
return_value=MagicMock(return_value=Organization(ANY, {}, {}, True)))
def test_create_repo_returns_true(self, get_organization, create_repo):
sut = GithubService("token")
actual = sut.create_repo_extra('repo-name', 'description', 'organization-name', 'team-name')
get_organization.assert_called() # ok
create_repo.assert_called() # failed
self.assertTrue(actual)
Since you mock your Github.get_organization you can use the MagicMock it returns directly rather than trying to mock another layer.
In this, I patch the same Github.get_organization, but avoid giving it a side effect or return value, and therefore pass it as an arg (like you did).
Then I create a convenience mock_organization and it will be the return value of the patched Github.get_organization.
Finally, the patch is checked like you did, and through the convenience mock_organization I check the create_repo method is called as well.
class TestGithubService(unittest.TestCase):
#patch("github.MainClass.Github.get_organization")
def test_create_repo_returns_true(self, mock_get_organization):
mock_organization = MagicMock()
mock_get_organization.return_value = mock_organization
sut = GithubService("token")
actual = sut.create_repo_extra(
"repo-name", "description", "organization-name", "team-name"
)
mock_get_organization.assert_called() # ok
mock_organization.create_repo.assert_called() # ok
self.assertTrue(actual)
Without seeing more of your code I am not sure why patching Organization did not work, but this is simpler, cleaner and just as effective.
I am using requests to create a requests.Session and set it up inside a function:
def create_web_session(cookie=None):
s = requests.Session()
if cookie is not None:
s.cookies.set("my_cookie_name", cookie)
return s
Sessions can be used as context managers. Can I use the function that returns the session (above) in a with statement?
with create_web_session("my_cookie_value") as s:
s.get(...)
Or would I have to change the function that instead takes a session and sets it up inside the context manager:
with requests.Session() as s:
setup_web_session(s, "my_cookie_value")
s.get(...)
Running the former seemed to work however my unit tests failed which is why I asked this question. I am patching the create_web_session and upon testing the s.get(...) I am asserting that my mock gets called with .get() however it seems to be session_mock.__enter__() that is calling it. Is this expected behavior?
Here is an example:
# Function
def my_function():
s = create_web_session()
s.get("https://google.com")
s.close()
# Test
#patch("foo.bar.create_web_session")
def test_my_function(self, mock_create_web_session):
my_function()
mock_create_web_session.assert_called_once()
mock_create_web_session.return_value.get.assert_called_once()
mock_create_web_session.return_value.close.assert_called_once()
Once I change the function to use context managers:
def my_function():
with create_web_session() as s:
s.get("https://google.com")
The test fails with:
Expected 'get' to have been called once. Called 0 times.
Your create_web_session is fine. The problem in the test is that while requests.Session.__enter__ simply returns back the same session, all methods on a mock return a fresh mock object. We can tell the mock to behave how we want and get a working test case like so:
def test_my_function(self, mock_create_web_session):
session = mock_create_web_session.return_value
session.__enter__.return_value = session
my_function()
mock_create_web_session.assert_called_once()
session.get.assert_called_once()
session.__exit__.assert_called_once()
Note that I assert __exit__ is called, not close, because the mock doesn't know anything about close or real sessions.
I'm using MagicMock to test a function in a web app. The function is imported directly from a module.
The expected behaviour is: when the function being tested is called, it calls on a third party api (but I'm mocking this for my test). That returns a dictionary and the function under test inserts that into an object and returns the object.
That works fine when I use nosetests to run that specific module of tests.
When I use nosetests to discover and run tests in my test/unit/ folder, the test doesn't work as expected. Instead the mocked API returns a NoneType and the function being tested returns a Magic Mock instance.
The test:
def test_get_user_facebook_data_1(self):
facebook_oauth_response = {u'name': u'Jack Jacker', u'email': u'jack#jack.jack', u'id': u'sd5Jtvtk6'}
facepy.GraphAPI.get = MagicMock(return_value=facebook_oauth_response)
user_facebook_data = user_service.get_user_facebook_data('bogus_facebook_oauth_access_token')
self.assertEquals(user_facebook_data._facebook_oauth_id, u'sd5Jtvtk6')
self.assertEquals(user_facebook_data._email, u'jack#jack.jack')
self.assertEquals(user_facebook_data._full_name, u'Jack Jacker')
The function being tested (in user_service module):
def get_user_facebook_data(facebook_access_token):
'''
With a user's FB access token, retrieve their credentials to either create a new account or login. Create a user object from the user model, but don't save
'''
try:
graph = facepy.GraphAPI(facebook_access_token)
facebook_data = graph.get('me?fields=id,name,email')
except facepy.exceptions.OAuthError:
raise errors.FacebookAccessTokenInvalidError()
user = user_model.User()
try:
facebook_oauth_id = facebook_data[u'id']
user.set_new_fb_oauth(facebook_oauth_id)
except KeyError:
raise errors.OauthNoIdError()
try:
email = facebook_data[u'email']
user.set_new_email(email)
except KeyError:
pass
try:
full_name = facebook_data[u'name']
user.set_new_full_name(full_name)
except KeyError:
pass
return user
Can you please help me understand why the result is inconsistent?
EDIT
New information - if I use nosetests on the module directly, the function I'm testing accesses the mocked Facepy dictionary values as unicode (as expected). If I user nosetests to discover tests, or if I use the solution posted by dm03514 below and run the tests directly, the function accesses the dictionary from the mocked facepy API as Magic Mock instances. Meaning, each result of accessing the dict is an Magic Mock instance.
That's confusing, as I set the return_value (in all tests) to be the dictionary.
Sorry long day, so can't really mentally parse through why things are working the way they currently are :p
But to solve it so it performs the same way regardless of where the test is executed, is to patch facepy import in the user_service module.
def test_get_user_facebook_data_1(self):
facebook_oauth_response = {u'name': u'Jack Jacker', u'email': u'jack#jack.jack', u'id': u'sd5Jtvtk6'}
with mock.patch('module.path.to.user_service.facepy') as mock_facepy:
mock_facepy.GraphAPI.return_vaule.get = MagicMock(return_value=facebook_oauth_response)
user_facebook_data = user_service.get_user_facebook_data('bogus_facebook_oauth_access_token')
self.assertEquals(user_facebook_data._facebook_oauth_id, u'sd5Jtvtk6')
self.assertEquals(user_facebook_data._email, u'jack#jack.jack')
self.assertEquals(user_facebook_data._full_name, u'Jack Jacker')
The above patches the facepy local to user_service module.
My issue was a misunderstanding about the way MagicMock handles dictionaries. You need to declare its __getitem__ property.
I think the "inconsistency" I mentioned was more of being a fluke that my tests worked at all.
This borrows heavily from #dm03514's answer.
def test_get_user_facebook_data_1(self):
facebook_oauth_response = {u'name': u'Jack Jacker', u'email': u'jack#jack.jack', u'id': u'sd5Jtvtk6'}
with mock.patch('api.services.user_service.facepy') as mock_facepy:
# Mocking the response from the facepy.
# Setting this side effect allows the Mock object to be accessed as a dict.
def getitem(name):
return facebook_oauth_response[name]
mock_oauth = MagicMock()
mock_oauth.return_value = facebook_oauth_response
mock_oauth.__getitem__.side_effect = getitem
mock_facepy.GraphAPI.return_value.get = mock_oauth
user_facebook_data = user_service.get_user_facebook_data('bogus_facebook_oauth_access_token')
self.assertEquals(user_facebook_data._facebook_oauth_id, u'sd5Jtvtk6')
self.assertEquals(user_facebook_data._email, u'jack#jack.jack')
self.assertEquals(user_facebook_data._full_name, u'Jack Jacker')
I'm writing test cases for code that is called via a route under Flask. I don't want to test the code by setting up a test app and calling a URL that hits the route, I want to call the function directly. To make this work I need to mock flask.request and I can't seem to manage it. Google / stackoverflow searches lead to a lot of answers that show how to set up a test application which again is not what I want to do.
The code would look something like this.
somefile.py
-----------
from flask import request
def method_called_from_route():
data = request.values
# do something with data here
test_somefile.py
----------------
import unittest
import somefile
class SomefileTestCase(unittest.TestCase):
#patch('somefile.request')
def test_method_called_from_route(self, mock_request):
# want to mock the request.values here
I'm having two issues.
(1) Patching the request as I've sketched out above does not work. I get an error similar to "AttributeError: 'Blueprint' object has no attribute 'somefile'"
(2) I don't know how to exactly mock the request object if I could patch it. It doesn't really have a return_value since it isn't a function.
Again I can't find any examples on how to do this so I felt a new question was acceptable.
Try this
test_somefile.py
import unittest
import somefile
import mock
class SomefileTestCase(unittest.TestCase):
def test_method_called_from_route(self):
m = mock.MagicMock()
m.values = "MyData"
with mock.patch("somefile.request", m):
somefile.method_called_from_route()
unittest.main()
somefile.py
from flask import request
def method_called_from_route():
data = request.values
assert(data == "MyData")
This is going to mock the entire request object.
If you want to mock only request.values while keeping all others intact, this would not work.
A few years after the question was asked, but this is how I solved this with python 3.9 (other proposed solutions stopped working with python 3.8 see here). I'm using pytest and pytest-mock, but the idea should be the same across testing frameworks, as long as you are using the native unittest.mock.patch in some capacity (pytest-mock essentially just wraps these methods in an easier to use api). Unfortunately, it does require that you set up a test app, however, you do not need to go through the process of using test_client, and can just invoke the function directly.
This can be easily handled by using the Application Factory Design Pattern, and injecting application config. Then, just use the created app's .test_request_context as a context manager to mock out the request object. using .test_request_context as a context manager, gives everything called within the context access to the request object. Here's an example below.
import pytest
from app import create_app
#pytest.fixture
def request_context():
"""create the app and return the request context as a fixture
so that this process does not need to be repeated in each test
"""
app = create_app('module.with.TestingConfig')
return app.test_request_context
def test_something_that_requires_global_request_object(mocker, request_context):
"""do the test thing"""
with request_context():
# mocker.patch is just pytest-mock's way of using unittest.mock.patch
mock_request = mocker.patch('path.to.where.request.is.used')
# make your mocks and stubs
mock_request.headers = {'content-type': 'application/json'}
mock_request.get_json.return_value = {'some': 'json'}
# now you can do whatever you need, using mock_request, and you do not
# need to remain within the request_context context manager
run_the_function()
mock_request.get_json.assert_called_once()
assert 1 == 1
# etc.
pytest is great because it allows you to easily setup fixtures for your tests as described above, but you could do essentially the same thing with UnitTest's setUp instance methods. Happy to provide an example for the Application Factory design pattern, or more context, if necessary!
with help of Gabrielbertouinataa on this article: https://medium.com/#vladbezden/how-to-mock-flask-request-object-in-python-fdbc249de504:
code:
def print_request_data():
print(flask.request.data)
test:
flask_app = flask.Flask('test_flask_app')
with flask_app.test_request_context() as mock_context:
mock_context.request.data = "request_data"
mock_context.request.path = "request_path"
print_request_data()
Here is an example of how I dealt with it:
test_common.py module
import pytest
import flask
def test_user_name(mocker):
# GIVEN: user is provided in the request.headers
given_user_name = "Some_User"
request_mock = mocker.patch.object(flask, "request")
request_mock.headers.get.return_value = given_user_name
# WHEN: request.header.get method is called
result = common.user_name()
# THEN: user name should be returned
request_mock.headers.get.assert_called_once_with("USERNAME", "Invalid User")
assert result == given_user_name
common.py module
import flask
def user_name():
return flask.request.headers.get("USERNAME", "Invalid User")
What you're trying to do is counterproductive. Following the RFC 2616 a request is:
A request message from a client to a server includes, within the first line of that message, the method to be applied to the resource, the identifier of the resource, and the protocol version in use.
Mocking the Flask request you need to rebuild its structure, what certainly, you will not to want to do!
The best approach should be use something like Flask-Testing or use some recipes like this, and then, test your method.
I have a test class and a setup function that looks like this:
#pytest.fixture(autouse=True, scope='function')
def setup(self, request):
self.client = MyClass()
first_patcher = patch('myclass.myclass.function_to_patch')
first_mock = first_patcher.start()
first_mock.return_value = 'foo'
value_to_return = getattr(request, 'value_name', None)
second_patcher = patch('myclass.myclass.function_two')
second_mock = second_patcher.start()
second_mock.return_value = value_to_return
#could clean up my mocks here, but don't care right now
I see in the documentation for pytest, that introspection can be done for a module level value:
val = getattr(request.module, 'val_name', None)
But, I want to be able to specify different values to return based on the test I am in. So I am looking for a way to introspect the test_function not the test_module.
http://pytest.org/latest/fixture.html#fixtures-can-introspect-the-requesting-test-context
You can use request.function to get to the test function. Just follow the link on the b wepage you referenced to see what is available on the test request object :)
Maybe the documentation has changed since the time of the accepted answer.
At least for me it was not clear how to
Just follow the link
So I thought I'd update this thread with the link itself:
https://pytest.org/en/6.2.x/reference.html#request
Edit December 2021
Even when the link is correct now I think this statement from the pytest documentation is just not correct:
Fixture functions can accept the request object to introspect the “requesting” test function ...
While I found some examples for getting attributes of the module I did not find a single working example of introspecting the test function that requests the fixture. May be related to collection and runtime order.
What really helped me to get the desired behavior was to use the factory idiom a little further down in the pytest documentation:
https://pytest.org/en/6.2.x/fixture.html#factories-as-fixtures
Set up the fixture factory
#pytest.fixture(scope='function')
def getQueryResult() -> object:
def _impl(_mrId: int = 7622):
return QueryResult(_mrId)
return _impl
Usage
# Concrete value
def test_foo(getQueryResult):
queryResult = getQueryResult(4711)
...
# Default value
def test_bar(getQueryResult):
queryResult = getQueryResult()
...