Fragments of verbose memory

冗長な記憶の断片 - Web技術のメモをほぼ毎日更新

Jan 1, 0001

title: “pytest –pdbの使い方: 失敗したテストでpdbを起動して原因を調べる” date: 2025-01-02T09:00:00+09:00 draft: false toc: false categories: [“日記”] tags: [“python”, “pytest”] author: “tumf” coverImage: “/images/python-pytest-pdb/cover-a.png” description: “pytest –pdbの使い方を解説。失敗したテストでpdbを起動する方法、-xとの組み合わせ、–trace、xdist利用時の注意点までまとめます。”

pytest で「失敗したテストのその場」に入りたいときは、まず pytest --pdb を使います。 テストが落ちた瞬間に pdb が開くので、変数の中身や呼び出し元をその場で確認できます。

print を足して再実行するより早く原因に辿り着けることが多いです。 この記事では、pytest公式ドキュメント をベースに、--pdb の基本、-x--trace との違い、pytest-xdist 利用時の注意点まで整理します。

まずこれだけ

  • 失敗した場所で止めたい: pytest --pdb
  • 最初の1件だけ見たい: pytest -x --pdb
  • テスト開始直後から追いたい: pytest --trace
  • pytest-xdist を使っている: -n 0 で並列実行を切ってからデバッグ

pytest --pdb の基本

pytest --pdb を使うと、エラー発生時に自動的に pdb (Python デバッカ) が起動し、その場でスタックトレースや変数の中身を確認できます。

実行例

以下のコードは、失敗したテストで pdb が起動する最小例です。ZeroDivisionError を意図的に出して、止まる位置を確認します。

1
2
3
4
def test_example():
    x = 10
    y = 0
    assert x / y == 0  # ZeroDivisionError が発生

このテストを実行する際に --pdb オプションを付けると:

1
pytest --pdb

エラーが発生した時点で pdb が起動します:

1
2
3
4
5
>>>>>>>>>>>>>>>> traceback >>>>>>>>>>>>>>>>
test_example.py:4: in test_example
    assert x / y == 0
E   ZeroDivisionError: division by zero
(Pdb)

pdb に入ったら、まず現在地と変数を見ます。 最初に覚えるコマンドは多くありません。

  • スタックトレースを確認:
1
(Pdb) where
  • 変数の中身をチェック:
1
2
(Pdb) print(x)
10

よく使う組み合わせ

最初の失敗だけ確認したい

テストが大量に落ちる状態で --pdb だけを付けると、失敗のたびに止まって操作量が増えます。 まずは最初の1件に絞るのが実用的です。

1
pytest -x --pdb

-x は最初の失敗でテスト実行自体を止めます。 原因調査の初手としては、この組み合わせが一番使いやすいです。

テスト開始直後から止めたい

失敗後ではなく、テスト関数の冒頭から1ステップずつ追いたいときは --trace を使います。 初期化処理やfixtureの入り口を見たいときに便利です。

1
pytest --trace

breakpoint()pdb.set_trace() を使いたい

コード側にブレークポイントを置いて止めたいなら、breakpoint()pdb.set_trace() を使います。 pytest はそのテストだけ出力キャプチャを外してくれるので、対話デバッグしやすいです。

1
2
3
4
def test_example():
    value = build_value()
    breakpoint()
    assert value.status == "ok"

失敗時に自動で止めるのが --pdb、自分で止めたい位置を決めるのが breakpoint() と考えると整理しやすいです。

注意点と副作用

--pdb は便利ですが、使用する際にいくつか注意すべき点があります。

1. 大量のテスト失敗への対応

失敗するテストが多い場合、それぞれの失敗でデバッガが起動するため作業が煩雑になります。

特定のテストに限定して実行することで解決できます:

1
pytest --pdb -k "test_target_case"

2. キーボード割り込み (Ctrl+C) の副作用

長時間停止するテストや無限ループ中のコードをデバッグする際、Ctrl+C による割り込みでデバッガを起動できます。ただし、外部リソース (例: スレッドやサブプロセス) を利用している場合、割り込みがリソース解放に影響を与える可能性があります。

対策として、後始末をテストコード側で明示しておくのが安全です。

  • リソース解放を確実にするため、finallypytest.fixtureyield 構文を利用しましょう。

3. CI/CD 環境での使用

自動化されたテスト環境では、 --pdb を有効にするとインタラクティブモードでテストが停止してしまいます。手動実行時のみ有効にする工夫が必要です。

以下の例は、ローカルで DEBUG=1 を付けたときだけ --pdb を有効にするやり方です。CI ではそのまま通常実行になります。

1
pytest ${DEBUG:+--pdb}

4. pytest-xdist との併用

pytest-xdist のドキュメントでは、分散実行中の PDB は標準入出力の制約で動かないとされています。 並列実行中に失敗を見つけたら、デバッグ時だけメインプロセス実行へ戻すのが基本です。

1
pytest -n 0 --pdb

-n 0pytest-xdistドキュメント でも、xdist を無効化してメインプロセスで走らせる方法として案内されています。

5. pytest-cov など他プラグインとの併用

カバレッジ収集や独自プラグインを多く入れている環境では、デバッグ中の体験が悪くなることがあります。 調査中だけ最小構成に寄せると、原因切り分けが早くなります。

実運用ではどう使うか

手元では、次の順番で使うことが多いです。

  1. まず pytest -x --pdb で最初の失敗だけ見る
  2. 必要なら -k で対象テストをさらに絞る
  3. 途中の状態を見たければ breakpoint() を入れる
  4. xdist を使っているなら -n 0 に戻す

[python](/tags/python/) のCLIをまとめて扱いたいなら、別記事の uv add/remove/syncの使い方: Pythonパッケージ管理を完全理解 も合わせてどうぞ。

デバッグ中の心得

デバッガの中では、状態を観察することを優先します。 変数を書き換えたり関数を実行したりすると、テスト環境そのものを変えてしまうことがあります。

終了時は q で抜けます。

1
(Pdb) q

よくある質問

pytest --pdbpytest --trace の違いは?

--pdb は失敗した瞬間に止まります。 --trace は各テストの開始時点で止まります。

失敗後の調査なら --pdb、テストの流れを最初から追うなら --trace が向いています。

pytest --pdb が止まらないときは?

次を確認してください。

  • そもそも失敗が発生しているか
  • pytest-xdist の並列実行を有効にしていないか
  • CI など対話入力できない環境で動かしていないか

特に xdist を使っているなら、まず pytest -n 0 --pdb を試すのが早いです。

特定のテストだけPDBで見たい

-k や node id を組み合わせると絞れます。

1
2
pytest --pdb -k login
pytest --pdb tests/test_auth.py::test_login

まとめ

pytest --pdb は、失敗したテストの文脈をその場で確認できるので、原因調査の初速をかなり上げてくれます。

  • まずは pytest -x --pdb
  • 開始地点から追うなら pytest --trace
  • 並列実行中なら pytest -n 0 --pdb
  • 必要に応じて breakpoint() を併用

この4点を覚えておくと、print を増やして何度も回すより短時間で問題に辿り着けます。