Help us understand the problem. What is going on with this article?

pytestでdifflibを使ってみた (例付き)

紹介

はじめてQiita記事を書きます、よろしくお願いします。

普段はECシステムを担当してます、自分のチームはテストをちゃんと書くのを決めたので、pytest導入をしようと思います。今回pytest導入したプロジェクトはメールテンプレートAPIです。ここのメールテンプレートAPIは、ECサイトで注文後、お客様にメールを届く文面生成処理のAPIです。

従来bashで実装されていたメール文面生成処理をPythonのAPI(Lambdaで実装)に移行した。

  • python移行したテストにあたって以下のような課題があった
    1. レスポンス項目のうちの一つであるメール文面をアサーションする必要があったが、内容が長いためassertを使うと結果が見辛い
    2. メール文面は注文元ショップ(Yahoo, Amazon...など)とメール種類(注文確認、出荷...など)の組み合わせにより沢山の種類があり、全ての組み合わせに対してテストケースを用意するのは大変
  • それぞれの課題について、以下の方法にて解決した
    1. メール文面のアサーションには、git diffコマンドの出力結果などにも用いられている標準ライブラリのdifflibを使ったところ、見慣れた形式で差分比較ができた
    2. 複数データに対するテストケースの定義は、pytestのparametetrizeアノテーションを用いた

pytestの標準test discoveryルール (詳細)

  • ファイル名はtest_*.py*_test.py
  • クラス名は Test で始まる必要があり
  • 関数、クラスのメソッドは先頭にプレフィックスtest_つける必要があり

Parametrizing

色々パラメーターをテストしたい時に、@pytest.mark.parametrizeは便利です。
この例で、沢山のグループ店舗、メールタイプ、注文番号をテストしたいので、1個テスト関数を作ればOKです。

手順

# pytestインストール
$ pip install pytest

# pytestインストールできた確認
$ pytest --version

# pytestを実行
# -vは結果を詳細に表示する
$ pytest -v

入力

1_1_2xxx.txt
xxx様

この度は「店舗」をご利用いただき、誠にありがとうございます。

ご注文内容は下記の通りです。test

商品発送後、改めて『商品の発送のお知らせ』メールを送信させていただきます。

コード

test_message.py
from pathlib import Path
import os
import difflib
import pytest

local_path = Path.cwd()
txt_dir = local_path.joinpath('data')


def get_testdata():
    """
    `data`フォルダーにファイル名からテストデータを作る

    Returns:
        list: parametrizeのtestdataとids
    """

    testdata = []
    testdata_ids = []
    for filename_with_path in txt_dir.glob('*_*_*.txt'):
        filename = os.path.basename(filename_with_path)
        mail_type = filename.split('_')[0]
        order_shop = filename.split('_')[1]
        order_no = filename.split('_')[2]
        testdata.append(tuple((order_shop, mail_type, order_no)))
        testdata_ids.append(f'{order_shop}_{mail_type}_{order_no}')
    return [testdata, testdata_ids]


testdata = get_testdata()[0]
testdata_ids = get_testdata()[1]


@pytest.mark.parametrize('order_shop, mail_type, order_no',
                         testdata,
                         ids=testdata_ids)
def test_mail_message(order_shop: str, mail_type: str, order_no: str):
    """
    shell作ったメールテンプレートとpythonのを比較する

    Args:
        order_shop (str): 店舗
        mail_type (str): メールタイプ
        order_no (str): 注文番号
    """

    # このプログラミングで作成したメールテンプレートを読み込む
    fromfile = '''xxx様

この度は「店舗」をご利用いただき、誠にありがとうございます。

ご注文内容は下記の通りです。

商品発送後、改めて『商品の発送のお知らせ』メールを送信させていただきます。'''

    # shellで作成したメールテンプレートを読み込む
    compare_file = local_path.joinpath('data').joinpath(
        f'{order_shop}_{mail_type}_{order_no}')
    if compare_file.is_file():
        with open(compare_file) as f:
            tofile = f.read()

    # 比較を実施
    diff = difflib.unified_diff(fromfile.splitlines(keepends=True),
                                tofile.splitlines(keepends=True),
                                fromfile='python-mail-api',
                                tofile='shell-mail-api')
    diff_str = ''.join(diff)
    print(diff_str)

    assert not diff_str

結果

image.png
メールの差分を見えます!

hands-lab
ハンズラボは小売業特化型ITソリューション企業です。数十万に及ぶ膨大な商品マスタを扱ってきた豊富なノウハウで、お客様の現場に最適なシステムを提案・開発します。 エンジニア募集中
https://www.hands-lab.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away