Issue
tl;dr
I am looking for the best practice for testing a flask app which uses external APIs under its routes, and probably this is where I have to simulate the external APIs using mock servers in the testing stage.
Ok, all I have got searching pytest-mock
and similar packages is that I can mock a function and have the results prepared for test assertions. But unfortunately I cannot find a best-practice explanation for my situation:
I have a flask app, under some of the routes of which I use external APIs and I want to use mock servers replacing those real external API URLs. I have come to two apparently fair solutions:
Using test URLs when testing
What I have got so far is that I can set different URLs dynamically by app.config
so that in my conftest.py
I can invoke using test URLs, and in reality, my app will be launched by the main URLs. Now my question here is how I can run a mock server automatically in the pytest fixture
setup stage to occupy the test URLs localhost ports. I said automatically because I can naively run a manual mock app in background myself, which is silly and useless for automated testing purposes
Using a mock function instead of sending the request to the main URL
I have realized that I can setup a mocker which is a function returning my desired value, and use it alongside calling my to-be-tested app. but the point is I want to use the mock inside my app, not comparing the returning value with a mock generated value
So where am I and what should I do?!
Here is a sample:
My flask app:
app = Flask(__name__)
@app.route('/test')
def test():
value = request.args.get('dummy_val')
# using external APIs
# This is where I need mock server
output = requests.get(
'http://external.api/some/url',
params={'val':value}
)
return output*2
my test file:
def test_can_double_value(test_client):
result = test_client('/test', query_string={'dummy_val':'foo'})
# test if status code == 200
# test if string equals to something
# blah blah blah
Solution
Thanks to jonrsharpe I now am aware that this can be done using responses library. Now if we want to mock an API in the fixture, it can be done as follows:
in conftest.py
import response
from pytest import fixture
@fixture
def client():
# add response
responses.add(responses.GET,
'http://mock.api/v',
json={'key': 'value'},
status=200
)
# do fixture stuff -> here it is yielding app test client
app.config['TESTING'] = True
client = app.test_client()
yield client
in test_module.py
import response
@response.activate
def test_can_connect(client):
result = client.get('/test')
assert result.status_code == 200
and in app.py
@app.route('/test')
def test():
res = requests.get('http://mock.api/v')
# blah blah
Comment in responses
documentation it is suggested that we add a response in the test function. But as I wanted to add it in a fixture, this is how I did it.
Answered By – Alireza
This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0