テストコード用のデータ作成でズボラしたら、テストがPASSしちゃった話
LambdaでCSV形式のデータを扱うことがありました。 その処理に対するテストコードを書いていたのですが、少し楽をしたがためにテストがPASSしちゃったのです。
起こったこと
CSV(テキストデータ)の各行に対して、「列数が期待通りでなければNG」の判定を行っていました。(下記関数は簡略化しています)
- 1行ずつ取り出して
- 1要素ずつに分解して
- 要素数をチェックする
def is_validate_csv_format(raw_data: str): for line in raw_data.split('\n'): item = line.split(',') if len(item) != 3: return False return True
このとき、下記のテストコードでテストを行ってPASSしました。「改行文字付きの文字列」を作成するのを楽にするため、join()
を使って改行文字を結合しています。
import pytest import app class TestValidateCsvFormat(object): @pytest.mark.parametrize( 'csv, expected', [ ( '\n'.join([ 'aaa,bbb,ccc', 'xxx,yyy,zzz' ]), True ), ( '\n'.join([ 'aaa,bbb', 'xxx,yyy,zzz' ]), False ), ] ) def test_normal(self, csv, expected): ret = app.validate_csv_format(csv) assert expected == ret
しかし、処理内容として下記のような「改行文字が末尾にあるCSVデータ」は問題ないCSVデータとしたいのですが、上記のテストではそもそもテストしていませんでした。その結果として、「改行文字が末尾にあるCSVデータ」を扱った際に不具合として現れたのです。
aaa,bbb,ccc\nxxx,yyy,zzz\n
どうしてこうなった
テストコードで扱うデータについて、楽をするためにjoin()
を使って改行文字を連結していたことが原因です。
join()
は便利な関数ですが、あくまでも連結です。そのため最後の文字として改行\n
は付与されません。
>>> '\n'.join(['111','222','333']) '111\n222\n333'
これによって、「改行文字が末尾にあるCSVデータ」に対するテストが漏れていたのです。
さきほどのテストコードについて、「改行文字が末尾にあるCSVデータ」のテストケースを1件追加して実行すると、追加したテストはFailedになりました。
import pytest import app class TestValidateCsvFormat(object): @pytest.mark.parametrize( 'csv, expected', [ ( '\n'.join([ 'aaa,bbb,ccc', 'xxx,yyy,zzz' ]), True ), ( '\n'.join([ 'aaa,bbb', 'xxx,yyy,zzz' ]), False ), ( 'aaa,bbb,ccc\naaa,bbb,ccc\n', True # これがFailedになる ), ] ) def test_normal(self, csv, expected): ret = app.validate_csv_format(csv) assert expected == ret
テストがPASSするようにコードを修正する
「Pythonの空文字列は偽となることを利用して、下記のように修正すれば楽だよ」と教えてもらいました。
def validate_csv_format(raw_data: str): for line in [x for x in raw_data.split('\n') if x]: item = line.split(',') if len(item) != 3: return False return True
>>> bool('') False
テストコードもjoin()
を使わずデータを作成し、PASSすることを確認しました。
import pytest import app class TestValidateCsvFormat(object): @pytest.mark.parametrize( 'csv, expected', [ ('aaa,bbb,ccc\nxxx,yyy,zzz', True), ('aaa,bbb\nxxx,yyy,zzz', False), ('aaa,bbb,ccc\naaa,bbb,ccc\n', True), ] ) def test_normal(self, csv, expected): ret = app.validate_csv_format(csv) assert expected == ret
教訓
テストコードで扱うデータは、めんどくさくてもしっかりと書きましょう。