【Python】Flask + pytestでCSV送信のテストをする

【Python】Flask + pytestでCSV送信のテストをする


# pytest # python

前回の記事では、FlaskでCSVを受け取ってpandasのDataFrame化するエンドポイントを作成しました。

今回はpytestを使ってそのエンドポイントをテストしてみたいと思います。

環境

Python==3.8.1

Flask==1.1.1

Werkzeug==1.0.0

目的

Flaskは最小限の機能だけを持つWebフレームワークですが、テスト機能がきちんと存在します。

そのテスト機能を使いこなして開発効率を向上させることが出来ますが、前回のようにCSVを受け付けるエンドポイントもテストもちろんテスト出来るので、その備忘録となります。

実際のコード

ディレクトリ構成のイメージ

my_project
├ app
│ └ main.py
└ test
  ├ confest.py
  └ test_app.py

pytest + Flaskの基本

Flaskにはテスト用のクライアントを作成する機能が付いているので、その機能を活用してあげます。 なお、with構文を使わないとうまくいかない模様です。

import pytest

from main import app as api


@pytest.fixture
def app():
    # Pycharmのテストでログをコンソールに出力するための設定
    import logging
    logging.basicConfig(level=logging.DEBUG)
    # DB初期化等の前処理
    # Pycharmでデバッガにエラーを拾わせるための設定
    api.config['PROPAGATE_EXCEPTIONS'] = True
    yield api
    # DB削除等の後処理


@pytest.fixture
def client(app):
    with app.test_client() as client:
        return client

以下のようにfixtureで作成したテストクライアントから、getやpostメソッドを呼び出すことでテストが行えます。 直感的で素晴らしいですね。requestsからリクエストを送信する際と似ているのでPython使いにとって有難い作りになってます。

def test_sample(client):
    res = client.post('/user/adress', )
    assert assert res.status_code == 200

CSVをpostで送信する

今回は一度DataFrameとしてCSVを読み込む形をとりました。 不要データの削除や、意図的なエラー発生のための事前にデータの加工が出来るので、DataFrameに一度落とす形が無難かと思います。

さて、client.postでデータを送信しますが、CSVとして認識させるためにはその際に一手間必要です。

  1. content_typeにmultipart/form-dataを指定
  2. BytesIOにしてからデータを送信する。

完成系は以下のようになります。

import io

import pandas as pd


def test_post_csv(client):
    df = pd.read_csv('test_data.csv')
    # index=Falseは必要有無に応じて
    csv_str = df.to_csv(index=False)
    client.post(
        '/',
        content_type='multipart/form-data',
        data={
            'data': (io.BytesIO(csv_str.encode()), 'test_data.csv')
        }
    )

BytesIOにしてからデータを送信するために、一度str型でcsvを出力(to_csvで出力先を指定しない場合はstr型で出力されます)し、その文字列をencodeしてbytes型にしています。

 

ちょっと手間取ってしまいましたが、これで快適なテスト生活に一歩近づけたような気がします。まだまだこれからですがね…。

参考

https://qiita.com/epire/items/b5ed1729d20c251d5544

https://flask.palletsprojects.com/en/1.1.x/testing/