Reputation: 370
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
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 namedroutes
which is in turn contained in a package namedmy_shining_app
, patch will be passed asmy_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