TL;DR
- pytestのtest IDにパラメータ由来の日本語を真っ当に表示するためには
pytest.ini
でdisable_test_id_escaping_and_forfeit_all_rights_to_community_support
をTrueに設定する必要がある - これを設定するとidsを指定すればきちんと表示されるようになるが、個別のidを設定すると表示が崩れる
何がしたかったか
パラメタライズドテストでパラメータごとに分かりやすい名前を日本語で付けたかった。
例えば、
@pytest.mark.parametrize(
"param",
[True, False],
ids=["真", "偽"],
)
def test_テスト(param: bool):
assert param
このようなテストでは、
test_テスト[偽] - assert False
と出力されてほしい。
現実にはどうなってしまうか
このまま実行すると
test_テスト[\u507d] - assert False
と、パラメータ由来の日本語(正確には非ASCII文字)はエスケープされた状態で出力されてしまう。
なお、これはparameterizeのIDに限らず、例えば
@pytest.mark.parametrize(
"param",
["真", "偽"],
)
def test_テスト2(param: str):
assert param == "真"
のようにパラメータがIDに使われる場合にでも、
test_テスト2[\u507d] - AssertionError: assert '偽' == '真'
このように残念な出力になってしまう。
(テスト名本体やassertの部分ではちゃんと日本語が表示できてるところが芸術点高いと思う)
どうして……
この問題はpytestのissueに挙げられている。
Is there anyway to not escape id in parametrization?
ざっと追ってみると、
Those test IDs are used in various places - on Windows, even printing them if they're non-ascii can be problematic, and we also potentially use them as filenames and in other problematic places.
(雑訳) これらのテストIDは色んな場所で使われる。Windowsでは、非ASCII文字を印字したりファイル名にしたり、その他いろんなことに使うと問題になることがある
というコメントがあり、Windowsなど一部のUnicodeの扱いに難がある環境に配慮してのことらしい。
90%以上のエンジニアが理想とする環境であるWindows()を無視するわけにもいかなかったのだろう
どうすればいいか?
pytestには、このエスケープを無効化するオプションが追加されている。
pytest.ini
ファイルに、
[pytest]
disable_test_id_escaping_and_forfeit_all_rights_to_community_support = True
を追加すれば、
@pytest.mark.parametrize(
"param",
[True, False],
ids=["真", "偽"],
)
def test_テスト(param: bool):
assert param
このテストケースで、 test_テスト[偽] - assert False
のように適切な表示がされるようになる。
残る問題
ただし、上記のような書き方は値とIDをバラバラにしてあるので、テストケースの追加・変更をしたいときに面倒である。
パラメータのセットに名前を付けたいというのがIDを付与するモチベーションであるので、当然、値とIDはセットで定義したい。
pytestでは、そのための記法が用意されている。
@pytest.mark.parametrize(
"param",
[
pytest.param(True, id="真"),
pytest.param(False, id="偽"),
],
)
def test_テスト(param: bool):
assert param
少々冗長になるが、まあ許容範囲である。
ところが、これを実行すると、 test_テスト[\u507d] - assert False
となり、 disable_test_id_escaping_and_forfeit_all_rights_to_community_support
を設定する前の状態に逆戻りしてしまう。
(このオプションを実装したときに考慮漏れだと思うので、そのうち修正PRを送ろうかなと思っている)
美しくない対処法1: パラメータに埋め込む
あまりキレイな対処方法ではないのだが、
- IDと値をセットで定義する
- 日本語も正しく出力する
を両立する方法もある。
ids(とid)を指定しない場合、test IDにはパラメータが羅列されるという仕様を利用して、名付けのためだけのパラメータを指定する。
@pytest.mark.parametrize(
"_id, param",
[("真", True), ("偽", False)],
)
def test_テスト(_id: str, param: bool):
assert param
のようにテストコードを書くことで、 test_テスト[偽-False] - assert False
と、一応は自分で付けた名前が正しく出力されるようになる。
美しくない対処法2: IDと値のセットを別で定義する
このようにパラメータを辞書型で定義しておき、値のセットをvaluesで、IDをkeysで取り出す。
_params_テスト = {
"真": True,
"偽": False,
}
@pytest.mark.parametrize(
"param",
list(_params_テスト.values()),
ids=list(_params_テスト.keys()),
)
def test_テスト(param: bool):
assert param
出力は test_テスト[偽] - assert False
のように、余計なパラメータが表示されない分、スッキリはする。