I am currrently writing a unit test for a function that processes a JSON string of filters to build a CSV file using flask-csv
. When I tested the function in the application it worked just fine, triggering the download with the right filters, and returning the file in the expected format.
Checking the output in response.data
also turns out fine, but I keep getting a BadRequest (400)
when running the test.
This would be the endpoint function:
csv-download.py
import json
import logging
import flask_csv
import pandas as pd
from flask import Blueprint
from jinja2 import Template
from pydantic import ValidationError
from models import CSVModel
home_blueprint = Blueprint("home", __name__, url_prefix="/")
@home_blueprint.route("csv_download", methods=["GET", "POST"])
def download_csv():
filter_str = request.args.get("filters")
content = request.args.get("content")
try:
model = CSVModel(csv_fliters=filter_str, csv_content=content)
except ValidationError as e:
raise BadInputException(e.json())
filters = json.loads(filter_str)
with open(
"bq_queries/myquery.bq", "r", encoding="utf-8"
) as file:
query_string = file.read()
query = Template(query_string)
# this function is replaced by a mock.patch in the test
data = load_from_query(apply_filters(query, filters, "None"), client)
today = pd.Timestamp.today().strftime("%Y%m%d")
start = pd.to_datetime(filters["order_date"][0]).strftime("%Y%m%d")
end = pd.to_datetime(filters["order_date"][1]).strftime("%Y%m%d")
response = flask_csv.send_csv(
data.to_dict(orient="records"),
f"{today}_{start}_until_{end}_{content}.csv".format(),
list(data.columns),
)
return response
This function is tested like this:
test_csv_download.py
import pytest
from flask.testing import FlaskClient
from unittest.mock import patch
@patch('customer_feedback_hub.home.views.load_from_query')
def test_csv_download_(mock_load, client: FlaskClient) -> None:
"""
Test verifying that the CSV-Download Endpoint works.
"""
import pandas as pd
import json
mock_load.return_value = pd.DataFrame({'A': [1,2], 'B': [3,4]})
test_filters = {'subentity_id': 1, 'order_date': ['2019-10-30', '2019-10-31']}
suffix = "test"
dl_filters = json.dumps(test_filters)
response = client.get("/csv_download" +
"?filters={}&content={}".format(dl_filters, suffix))
# response format taken directly from:
# https://github.com/Shir0kamii/Flask-CSV/blob/master/test_flask_csv.py
expected = b"""A,B
1,3
2,4
""".replace(b'\n', b"\r\n")
response.data == expected
assert response.status_code == 200
The message for the test failing:
====================================================================== FAILURES =======================================================================
_________________________________________________________________ test_csv_download_ __________________________________________________________________
mock_load = <MagicMock name='load_from_query' id='139751143251392'>, client = <FlaskClient <Flask 'customer_feedback_hub.app'>>
@patch('customer_feedback_hub.home.views.load_from_query')
def test_csv_download_(mock_load, client: FlaskClient) -> None:
"""
Test verifying that the CSV-Download Endpoint works.
"""
import pandas as pd
import json
mock_load.return_value = pd.DataFrame({'A': [1,2], 'B': [3,4]})
test_filters = {'subentity_id': 1, 'order_date': ['2019-10-30', '2019-10-31']}
suffix = "test"
# start = pd.to_datetime(test_filters["order_date"][0]).strftime("%Y%m%d")
# end = pd.to_datetime(test_filters["order_date"][1]).strftime("%Y%m%d")
dl_filters = json.dumps(test_filters)
response = client.get("/csv_download" +
"?filters={}&content={}".format(dl_filters, suffix))
# response format taken directly from:
# https://github.com/Shir0kamii/Flask-CSV/blob/master/test_flask_csv.py
expected = b"""A,B
1,3
2,4
""".replace(b'\n', b"\r\n")
response.data == expected
> assert response.status_code == 200
E assert 400 == 200
E -400
E +200
tests/home/test_views.py:79: AssertionError
Also, for the sake of completeness, the definition of CSVModel
used in the original function, which I use to validate that the string indeed can be loaded as a JSON string
models.py
import json
from pydantic import BaseModel, Extra, NoneStr, validator
class CSVModel(BaseModel):
"""
Example Pydantic model for validating json
"""
csv_filters: str
csv_content: NoneStr = None
class Config:
extra = Extra.forbid
@validator("csv_filters")
def filters_are_json(cls, v):
try:
dic = json.loads(v)
dic.items()
except Exception:
raise ValueError("No JSON!")
Maybe I'm missing something obvious, but I'd be happy to have another pair of eyes on this.
You have
response.data == expected
But you probably want
assert response.data == expected
And I suspect that assertion will fail and tell you more. :)