kjohnsen
kjohnsen

Reputation: 370

Can I use mocks in testing Flask app functions?

I'm trying to test some routes on my Flask app that call external APIs, which I want to mock.

The routes are set up like this:

@app.route('/url/<string::arg>')
def route_function(arg):
    data = external_api(arg)
    response = make_response(data)
    # configure response
    return response

I originally tried something like this:

class TestFlaskApp(unittest.TestCase):
    def setUp(self):
        self.app = app.test_client()

    @patch('external_api',
           side_effect=mock_api)
    def test_flask_route(self, api):
        result = app.get('/url/arg')
        self.assertEqual(result.status_code, 200)
        api.assert_called_once_with('arg')

...which failed. The mock API function wasn't called, since I assume the mock does not apply in the app context.

I also tried this, thinking I might be able to test the route functions directly and thus avoid having to use the app context:

class TestAppFunctions(unittest.TestCase):
    @patch('external_api',
           side_effect=mock_api)
    def test_flask_function(self, api):
        result = my_flask_app.route_function('arg')
        self.assertEqual(result.status_code, 200)
        api.assert_called_once_with('arg')

...but this didn't work either, since to make a response, route_function needs the app context.

So is there a way to mock inside the app context? How else can I test these routes without triggering external API calls?

Upvotes: 5

Views: 5785

Answers (1)

kjohnsen
kjohnsen

Reputation: 370

Oluwafemi Sule was right...I just needed to patch the function where it was used, not where it was defined.

You need to pass the object path to the patch function so that it can be resolved and replaced with the mock at runtime. For example if external_api function is called in a module named routes which is in turn contained in a package named my_shining_app, patch will be passed as my_shining_app.routes.external_api

Note that the path should be where the function is called (i.e. where it's to be replaced with the mock) and not where it's defined

Upvotes: 6

Related Questions