2026年10月27日、Pythonのテストで最もよく使われているツールpytestの9.2.0が公開されました。6月の9.1.1から、およそ4か月ぶりの新しい版です。
今回は、初心者がつまずきやすいところを助けてくれる変更が目立ちます。parametrizeの書き間違いが、意味のわかるエラーになりました。
テストが失敗したときの差分にも、+と-が何を表すかの説明が付いています。今回は、9.1.1と9.2.0で同じテストを動かしながら、何が変わったのかを見ていきます。
まずは9.2で何が変わったのかを整理しよう¶
最初に、9.2.0の主な変更を表にまとめます。内容は、pytestの公式リポジトリにある変更履歴changelog.rstにもとづいています。
| 分類 | 主な変更 |
|---|---|
| 互換性を壊す変更 | parametrizeのindirect、ids、scopeをキーワード専用に |
| 互換性を壊す変更 | プラグインを読み込めないときの終了コードを整理 |
| 新機能 | Full diffに、+と-の意味の説明を表示 |
| 新機能 | 長い文字列のパラメーターからテスト名を作る方法を選べる設定 |
| 改善 | アサーションの書き換え結果のキャッシュを、時刻ではなく中身で判定 |
| 不具合の修正 | 入れ子のデータを渡したpytest.approxがTypeErrorを出すように |
PyPIの記録によると、9.2.0が必要とするPythonは、これまでどおり3.10以上です。最近は3.10を外すライブラリが多い中、pytestはまだ3.10に対応しています。
pytestそのものの使い方は、こちらで紹介しています。【関連記事】pytest入門|Pythonでテストを書く基本
parametrizeの書き間違いが、わかりやすいエラーに¶
まずは、互換性を壊す変更から見ていきましょう。parametrizeは、1つのテスト関数を、いくつもの値の組み合わせで繰り返し動かす仕組みです。
変更履歴によると、parametrizeのindirect、ids、scopeの3つの引数が、キーワード専用になりました。つまり、ids=[...]のように、名前を付けて渡す必要があります。
よくある書き間違い¶
変更履歴では、よくある間違いの例として、引数の名前を別々の文字列で書いてしまうケースが挙げられています。実際に、その書き方を試してみましょう。
import pytest
@pytest.mark.parametrize("a", "b", [(1, 2)])
def test_add(a, b):
assert a < b
本当は"a, b"と1つの文字列にまとめるべきところを、"a"と"b"に分けてしまっています。9.1.1と9.2.0を、それぞれ新しい仮想環境に入れ、手元のPython 3.11で動かした結果です。
pytest 9.1.1
In test_param.py::test_add: indirect fixture '(1, 2)' doesn't exist
pytest 9.2.0
TypeError: Metafunc.parametrize() takes 3 positional arguments but 4 positional arguments (and 1 keyword-only argument) were given
9.1.1では、(1, 2)という名前のフィクスチャがない、というエラーでした。値として渡したつもりの[(1, 2)]が、3番目のindirectという引数に入ってしまったためです。
これでは、何を間違えたのか見当がつきませんよね。9.2.0では、位置で渡せる引数の数を超えているというTypeErrorになり、引数の数がおかしいことがすぐわかります。
正しい書き方¶
正しくは、引数の名前を1つの文字列にまとめ、idsなどは名前を付けて渡します。9.2.0で試した書き方です。
import pytest
@pytest.mark.parametrize("a, b", [(1, 2), (3, 5)], ids=["small", "large"])
def test_add(a, b):
assert a < b
pytest -vで実行すると、次のように2つのテストとして動きました。
test_param_ok.py::test_add[small] PASSED
test_param_ok.py::test_add[large] PASSED
idsで付けた名前が、角かっこの中に表示されています。これまでidsを名前なしで渡していた方は、ids=を付けるように直しておきましょう。
長い文字列のテスト名は短くなった¶
parametrizeでは、渡した値から、角かっこの中のテスト名が自動で作られます。変更履歴によると、9.2.0では新しい設定parametrize_long_str_id_strategyが加わり、既定のshortでは、100文字を超える文字列は引数名と番号の形に置き換わります。
150文字の文字列を渡したテストで、pytest --collect-only -qを実行すると、テスト名は次のようになりました。
| 版 | 2つ目のテストの名前 |
|---|---|
| 9.1.1 | test_length[xxxx…](150文字がそのまま並ぶ) |
| 9.2.0 | test_length[text1] |
長い値をそのまま名前にすると、画面からはみ出して読みにくくなります。CIの結果でテスト名を探すときにも、短いほうが扱いやすいでしょう。
差分の+と-に説明が付いた¶
続いて、新機能です。変更履歴によると、Full diffという差分の表示に、+と-の記号が何を表すかの説明が付きました。
実際に失敗させてみた¶
リストの比較が失敗するテストを用意して、pytest -vvで詳しい差分を表示させてみます。
def test_fruits():
assert ["apple", "banana", "melon"] == ["apple", "orange", "melon"]
9.1.1と9.2.0で、差分の部分を比べた結果です。
pytest 9.1.1
Full diff:
[
'apple',
- 'orange',
+ 'banana',
'melon',
]
pytest 9.2.0
Full diff: (-: missing in left side, +: extra in left side)
[
'apple',
- 'orange',
+ 'banana',
'melon',
]
9.2.0では、Full diffの横に、-は左側に足りないもの、+は左側に余計なもの、という説明が加わりました。ここでいう左側とは、==の左に書いた値、つまり実際の結果のことです。
なぜ嬉しいのか¶
差分の+と-は、どちらが期待した値で、どちらが実際の値なのか、慣れないうちは混乱しがちです。説明が毎回表示されるので、記号の意味を覚えていなくても読めるようになりました。
assertの書き方や、失敗したときの表示の読み方は、こちらで詳しく紹介しています。【関連記事】pytestのassert文の使い方解説
pytest.approxに入れ子のデータを渡すとエラーに¶
小数の比較で使うpytest.approxにも、大事な修正が入っています。approxは、0.1+0.2のように、ごくわずかな誤差が出る計算を、許容範囲付きで比べるための関数です。
変更履歴によると、これまでは、リストの中に辞書を入れたような入れ子のデータを渡すと、エラーにならずに受け付けられていました。ところが、比べると必ず等しくないと判断され、許容範囲が黙って無視されていたそうです。
2つの版で比べてみた¶
実際に、リストの中に辞書が入ったデータで試しました。
import pytest
def test_scores():
assert [{"score": 0.1 + 0.2}] == pytest.approx([{"score": 0.3}])
実行した結果の、エラーの部分です。9.1.1の表示は、要点だけに短くしています。
pytest 9.1.1
AssertionError: assert [{'score': 0....000000000004}] == approx([{'score': 0.3}])
(pytest_assertion plugin: representation of details failed: ...)
pytest 9.2.0
TypeError: pytest.approx() does not support nested data structures: {'score': 0.3} at index 0
9.1.1では、テストが失敗したうえに、詳しい表示を作る段階でも内部のエラーが出ていました。9.2.0では、approxは入れ子のデータに対応していない、とはっきり教えてくれます。
入れ子のデータを比べたいときは¶
入れ子のデータを比べたいときは、中の値ごとにapproxを使います。たとえばresult[0]["score"] == pytest.approx(0.3)のように、小数の部分だけを取り出して比べるとよいでしょう。
CIでテストが速くなるかもしれない変更¶
もう1つ、実務で効いてきそうな改善も紹介します。pytestは、assertの失敗をわかりやすく表示するために、テストのファイルを読み込むときに書き換えています。
書き換えた結果は、__pycache__というフォルダにキャッシュとして保存されます。変更履歴によると、このキャッシュが使えるかどうかを、ファイルの更新時刻ではなく、中身から計算したハッシュ値で判断するようになりました。
なぜCIで効くのか¶
GitHub Actionsのような、コードを変更するたびに自動でテストを動かす仕組みを、CI(継続的インテグレーション)と呼びます。CIでは、毎回リポジトリを取り出し直すので、すべてのファイルの更新時刻が新しくなります。
変更履歴によると、そのせいで、保存しておいたキャッシュがまるごと捨てられ、すべてのテストファイルが毎回書き換え直されていたそうです。中身で判断するようにしたことで、1ファイルあたりおよそ15マイクロ秒の計算と引き換えに、キャッシュが生き残るようになったと説明されています。
GitHub Actionsでテストを自動で動かす方法は、こちらで紹介しています。【関連記事】GitHub Actions入門
そのほかの変更¶
ほかにも、たくさんの改善と修正が入っています。初心者にも関係しそうなものを、表にまとめます。
| 変更 | 中身 |
|---|---|
-cの扱い |
存在しない設定ファイルを指定すると、使い方の誤りとしてエラーに |
| strict_markers | 有効なとき、-mで指定したマーカー名も登録済みかを確かめる |
| tmp_path | 一時フォルダが多いときに、どんどん遅くなる問題を修正 |
| pytest.toml | [pytest]の表がないまま書いた設定を、黙って無視せずエラーに |
| yield_fixture | 古い書き方として、pytest 10か11で取り除く予定に |
どれも、黙って無視されていた間違いを、その場で教えてくれる方向の変更です。
上げる前にやっておきたいこと¶
最後に、pytestを9.2.0へ上げる前に確かめておきたいことを整理します。
| 順番 | 確認すること |
|---|---|
| 1 | parametrizeのids、indirect、scopeを、名前なしで渡していないか |
| 2 | pytest.approxに、入れ子のリストや辞書を渡していないか |
| 3 | -cで指定している設定ファイルが、本当に存在するか |
| 4 | 使うpytestの版を、requirements.txtなどで固定しているか |
私は10年ほどエンジニアとして開発に関わってきましたが、parametrizeの引数の名前を分けて書いてしまい、意味のわからないフィクスチャのエラーで30分ほど悩んだことがあります。今回の変更で、同じところでつまずく人はぐっと減るはずです。
まとめ¶
2026年10月27日、pytest 9.2.0が公開されました。parametrizeの書き間違いがわかりやすいTypeErrorになり、差分には+と-の意味の説明が付き、入れ子のデータを渡したapproxもはっきりエラーを出すようになっています。
どれも、テストを書く人がつまずきにくくなる、嬉しい変更です。上げたあとにエラーが出たら、それは今まで見過ごしていた書き間違いかもしれません。
ここまでお読みいただきありがとうございました。