Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 238
wizeowl Avatar asked Apr 19 '16 21:04

wizeowl


People also ask

How do you request a mock Flask in Python?

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.

How do you send a mock request in Python?

To mock the requests module, you can use the patch() function. Suppose that the mock_requests is a mock of the requests module. The mock_requests. get() should return a mock for the response.

What does mock mock () do?

mock provides a powerful mechanism for mocking objects, called patch() , which looks up an object in a given module and replaces that object with a Mock . Usually, you use patch() as a decorator or a context manager to provide a scope in which you will mock the target object.


2 Answers

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.

like image 86
Hakan Baba Avatar answered Nov 16 '22 01:11

Hakan Baba


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!

like image 35
Brady Perry Avatar answered Nov 16 '22 02:11

Brady Perry