For developers and QE engineers dealing with asynchronous systems, testing can be a significant challenge. These systems often respond with only a request ID, making it difficult to verify the actual response data. However, by leveraging the power of Flask and pytest, we can create a robust testing framework that simplifies the process.
The asynchronous system challenge
In an asynchronous system, when a request is sent, the system typically responds with a request ID rather than the actual data. This ID is then used to query the system for the result at a later time. This behavior can make testing difficult, as the traditional request-response cycle is disrupted.
Introducing Flask as a mock system
Flask is a lightweight Python web framework that can be used as a mock system to simulate the asynchronous system’s behavior. In our testing setup, the system under test will send its responses to Flask instead of the actual system, or a callback route will be notified. Flask will act as a piggyback instance, storing the responses for later verification.
Flask and pytest integration
It’s no secret that I find pytest to be a powerful and flexible testing framework written in Python, making it an ideal choice for our testing needs. By integrating Flask with pytest, we can create a streamlined testing process that leverages the strengths of both tools.
Setting up the Flask mock system
Let’s start by creating a Flask application that will act as our mock system. This application will have endpoints to receive responses from the system under test and retrieve them later using the request ID.
# mock_system.py
from flask import Flask, request, jsonify
app = Flask(__name__)
responses = {}
@app.route('/receive', methods=['POST'])
def receive_response():
data = request.get_json()
request_id = data['request_id']
response = data['response']
responses[request_id] = response
return jsonify({'status': 'success'})
@app.route('/retrieve/<request_id>', methods=['GET'])
def retrieve_response(request_id):
if request_id in responses:
return jsonify(responses[request_id])
return jsonify({'error': 'Request ID not found'}), 404
In this example, we create a Flask application with two endpoints:
/receive- accepts POST requests containing a request ID and a response. It stores the response in a dictionary, using the request ID as the key./retrieve/<request_id>- accepts GET requests with a request ID as a parameter. It retrieves the corresponding response from the dictionary and returns it as JSON. If the request ID is not found, it returns a 404 error.
Writing tests with pytest and integration with the Flask mock
Next, we use pytest to write our tests. Within each test, we send requests to the system under test and capture the request IDs. Using these IDs, we then query the Flask mock system to retrieve the responses and assert their correctness.
# tests/test_async_system.py
import pytest
import requests
BASE_URL = 'http://localhost:5000'
@pytest.fixture(scope='module')
def mock_system():
from mock_system import app
app.testing = True
with app.test_client() as client:
yield client
def test_async_operation(mock_system):
# Send request to the system under test
request_id = send_request_to_system()
# Wait for the response to be available
# ... some logic here
# Retrieve the response from the mock system
response = mock_system.get(f'{BASE_URL}/retrieve/{request_id}')
data = response.get_json()
# Assert the response data
assert data['status'] == 'success'
assert data['result'] == 'expected_result'
In this test:
- The
mock_systemfixture sets up the Flask application in testing mode and yields a test client. - We send a request to the system under test and capture the request ID.
- We wait for the response to be available (delay, polling, or callback).
- We use the
mock_systemfixture to GET/retrieve/<request_id>. - We assert that the response data meets our expectations.
Benefits of this approach
Simplified testing. The Flask mock system abstracts away the complexities of the actual asynchronous system, letting us focus on testing the core functionality of our code.
Improved reliability. With Flask acting as a controlled environment, we ensure consistent test results and eliminate issues caused by the real system’s availability or state.
Rapid feedback. pytest’s fast execution and comprehensive reporting provide quick feedback, enabling us to identify and fix issues efficiently.
Scalability. As our system grows, the testing framework scales by adding more test cases and taking advantage of pytest’s parallelization and distributed testing, using more fixtures of the same mocked service.
Conclusion
Testing asynchronous systems is challenging, but Flask plus pytest gives you a robust testing framework that streamlines the process. With Flask acting as a mock and pytest handling the testing logic, you ensure reliability and correctness of your code while benefiting from rapid feedback and scalability.
