Using mock in testing of django application to override a function - python

I have view function that uses nmap to scan the devices in the network.
views.py
import nmap
def home(request):
y=nmap.PortScanner()
data = y.scan(hosts="192.168.1.*", arguments="-sP")
context[status]=data['status']['addresses']['ipv4']
return render_template('home.html',context)
Now i want to test this for no devices, 1 device connected and 2 or more device connected. I need to override data in tests.py.
I was thinking that it can be done using mock function. I can override it in tests.py but when simulate responses it not get override in view function.
How can i test this nmap function ?

Monkey patching would be a good solution in your case.
Also have a look at this SO question about monkey patching
here is a possible implementation, of course you need to integrate this into your test framework.
import your_module
class MockPortScanner(object):
# by setting this class member
# before a test case
# you can determine how many result
# should be return from your view
count = 0
def scan(self, *args, **kwargs):
return {
'status': {
'addresses': {
'ipv4': [i for i in range(self.count)]
}
}
}
def your_test_method():
MockPortScanner.count = 5
request = None # create a Mock Request if you need
# here is the mocking
your_module.nmap.PortScanner = MockPortScanner
# call your view as a regular function
rv = your_module.home(request)
# check the response
UPDATE
To have the original PortScanner later in other parts of tests, save it in the tests after importing nmap.
import nmap
OriginalPortScanner = nmap.PortScanner
Then, you will able to select the PortScanner (either original or mock) like:
views.nmap.PortScanner = OriginalPortScanner

Related

Python test patch is never called

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.

Mocking a Sqlalchemy session for pytest

I don't know if this can be done but I'm trying to mock my db.session.save.
I'm using flask and flask-alchemy.
db.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
The unit test
def test_post(self):
with app.app_context():
with app.test_client() as client:
with mock.patch('models.db.session.save') as mock_save:
with mock.patch('models.db.session.commit') as mock_commit:
data = self.gen_legend_data()
response = client.post('/legends', data=json.dumps([data]), headers=access_header)
assert response.status_code == 200
mock_save.assert_called()
mock_commit.assert_called_once()
And the method:
def post(cls):
legends = schemas.Legends(many=True).load(request.get_json())
for legend in legends:
db.session.add(legend)
db.session.commit()
return {'message': 'legends saved'}, 200
I'm trying to mock the db.session.add and db.session.commit. I've tried db.session.save and legends.models.db.session.save and models.db.session.save. They all came back with the save error:
ModuleNotFoundError: No module named 'models.db.session'; 'models.db' is not a package
I don't get the error and I'm not sure how to solve it.
Or am I doing something that is totally wrong in wanting to mock a db.session?
Thanks.
Desmond
The problem you're running into here is better served by restructuring your code so that it's more testable rather than mocking out every component, or otherwise making a (very) slow integration test. If you get in the habit of writing tests in that way, then over time you'll end up with a slow build that will take too long to run, and you'll end up with fragile tests (good talk on the subject of why fast tests are important here).
Let's take a look at this route:
def post(cls):
legends = schemas.Legends(many=True).load(request.get_json())
for legend in legends:
db.session.add(legend)
db.session.commit()
return {'message': 'legends saved'}, 200
...and decompose it:
import typing
from flask import jsonify
class LegendsPostService:
def __init__(self, json_args, _session=None) -> None:
self.json_args = json_args
self.session = _session or db.session
def _get_legends(self) -> Legend:
return schemas.Legends(many=True).load(self.json_args)
def post(self) -> typing.List[typing.Dict[str, typing.Any]]:
legends = self._get_legends()
for legend in legends:
self.session.add(legend)
self.session.commit()
return schemas.Legends(many=True).dump(legends)
def post(cls):
service = LegendsPostService(json_args=request.get_json())
service.post()
return jsonify({'message': 'legends saved'})
Notice how we've isolated nearly all the points of failure from post into LegendsPostService, and further, we've removed all the flask internals from it as well (no global request objects floating around, etc). We've even given it the ability to mock out session if we need to for testing later on.
I would recommend you focus your testing efforts on writing test cases for LegendsPostService. Once you've got excellent tests for LegendsPostService, decide if you believe that even more test coverage will add value. If you do, then consider writing one simple integration test for post() to tie it all together.
The next thing you need to consider is how you want to think about SQLAlchemy objects in tests. I recommend just using FactoryBoy for auto-creating "mock" models for you. Here's a full application example for how to setup flask / sqlalchemy / factory-boy in this way: How do I produce nested JSON from database query with joins? Using Python / SQLAlchemy
Here's how I'd write a test for LegendsPostService (apologies as this is a bit hasty and doesn't perfectly represent what you're trying to do - but you should be able to adjust these tests for your use case):
from factory.alchemy import SQLAlchemyModelFactory
class ModelFactory(SQLAlchemyModelFactory):
class Meta:
abstract = True
sqlalchemy_session = db.session
# setup your factory for Legends:
class LegendsFactory(ModelFactory):
logo_url = factory.Faker('image_url')
class Meta(ModelFactory.Meta):
model = Legends
from unittest.mock import MagicMock, patch
# neither of these tests even need a database connection!
# so you should be able to write HUNDREDS of similar tests
# and you should be able to run hundreds of them in seconds (not minutes)
def test_LegendsPostService_can_init():
session = MagicMock()
service = LegendsPostService(json_args={'foo': 'bar'}, _session=session)
assert service.session is session
assert service.json_args['foo'] == 'bar'
def test_LegendsPostService_can_post():
session = MagicMock()
service = LegendsPostService(json_args={'foo': 'bar'}, _session=session)
# let's make some fake Legends for our service!
legends = LegendsFactory.build_batch(2)
with patch.object(service, '_get_legends') as _get_legends:
_get_legends.return_value = legends
legends_post_json = service.post()
# look, Ma! No database connection!
assert legends_post_json[0]['image_url'] == legends[0].image_url
I hope that helps!

mock xmlrpc.client method python

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.

Mock flask.request in python nosetests

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.

Python mock patch doesn't work as expected for public method

I'm trying to patch a public method for my flask application but it doesn't seem to work.
Here's my code in mrss.feed_burner
def get_feed(env=os.environ):
return 'something'
And this is how I use it
#app.route("/feed")
def feed():
mrss_feed = get_feed(env=os.environ)
response = make_response(mrss_feed)
response.headers["Content-Type"] = "application/xml"
return response
And this is my test which it's not parsing.
def test_feed(self):
with patch('mrss.feed_burner.get_feed', new=lambda: '<xml></xml>'):
response = self.app.get('/feed')
self.assertEquals('<xml></xml>', response.data)
I believe your problem is that you're not patching in the right namespace. See where_to_patch documentation for unittest.mock.patch.
Essentially, you're patching the definition of get_feed() in mrss.feed_burner but your view handler feed() already has a reference to the original mrss.feed_burner.get_feed(). To solve this problem, you need to patch the reference in your view file.
Based on your usage of get_feed in your view function, I assume you're importing get_feed like so
view_file.py
from mrss.feed_burner import get_feed
If so, you should be patching view_file.get_feed like so:
def test_feed(self):
with patch('view_file.get_feed', new=lambda: '<xml></xml>'):
...

Categories

Resources