Search code examples
pythonflaskpytestpython-unittestpython-mock

Is there a way to mock flask request object inside a function?


I need to test the response of a method and this method calls 2 different methods. The response of the method is created regarding to these 2 inner methods. 1 of the inner method uses flask request object as an input in its implementation.

The code sample explains it better:

class ApiDetail():

    def process_data(self, process_input):
        try:
            request_simple_url = self.handle_url(request.base_url, request.view_args)
            return self.validate_input(request_simple_url, process_input)
        except NotFoundException as e:
            return {"error": str(e)}, HTTPStatus.UNAUTHORIZED
        #implementation detail


    @staticmethod
    def handle_url(request_base_url, request_arguments):
        #implementation detail


def test_process_data():
    with mock.patch('ApiDetail.validate_input', return_value = NotFoundException):
        with mock.patch('ApiDetail.handle_url', return_value='test_handled'):
            assert ApiDetail.process_data(self=ApiDetail, process_input='test_input') == NotFoundException

I'm trying to test process_data method. If there is an exception on validate_input method, I assert process_data returns exception, too. There is a problem in this line, I couldn't mock request object because it's not an input of process_data method:

request_simple_url = self.handle_url(request.base_url, request.view_args)

Error message is clear but I couldn't find the way:

name = 'request'

def _lookup_req_object(name):
    top = _request_ctx_stack.top
    if top is None:
        raise RuntimeError(_request_ctx_err_msg)
RuntimeError: Working outside of request context. This typically means that you attempted
to use functionality that needed an active HTTP request.      
Consult the documentation on testing for information about how to avoid this problem.

If handle_url is implemented without any input value, it will work properly but it's required to receive the request object attributes from out of the method.

Is there any way to solve the problem?


Solution

  • You can use your global request object outside of the process_data method, and pass its attributes to your method:

    class ApiDetail():
    
        base_url = request.base_url
        view_args = request.view_args
    
        def process_data(self, process_input, base_url, view_args):
            try:
                request_simple_url = self.handle_url(base_url, view_args)
                return self.validate_input(request_simple_url, process_input)
            except NotFoundException as e:
                return {"error": str(e)}, HTTPStatus.UNAUTHORIZED
            #implementation detail
    
        @staticmethod
        def handle_url(request_base_url, request_arguments):
            #implementation detail
    
    
    def test_process_data():
        with mock.patch('ApiDetail.validate_input', return_value = NotFoundException):
            with mock.patch('ApiDetail.handle_url', return_value='test_handled'):
                assert ApiDetail.process_data(self=ApiDetail, process_input='test_input', base_url='test_url', view_args='test_args') == NotFoundException