先に結論
- pytest は、失敗した
assertの比較対象をそのまま表示します。ヘッダーの dict を比べると、Authorization: Bearer <キー>が失敗ログに出ました。 -
-l(--showlocals)を付けると、比較と関係ないローカル変数まで表示されます。assert status == 200が失敗しただけで、同じ関数内のheadersに入ったキーが出ました。 -
--junitxmlで保存したレポートにも同じ内容が入りました(-lありで5か所、なしで2か所)。 -
__repr__を'***'にした入れ物に入れると、-l -vvでもキーは出ませんでした。--tb=noでも出ませんでした。
確認環境: Python 3.13.5 / pytest 9.2.0(Linux)。キーはダミー値 sk-DUMMY-1234567890abcdef です。
なぜ気にするか
AIエージェントにテストを回させると、失敗ログはそのままエージェントの会話に入り、CI ならログやアーティファクトとして残ります(どこまで残るかはサービスの設定によります)。テストが失敗したときのログに鍵が出ないかは、先に確かめておく価値があります。
試したテスト
import os
def build_headers():
key = os.environ["DUMMY_API_KEY"]
return {"Authorization": f"Bearer {key}"}
def test_status():
headers = build_headers()
status = 401 # 失敗を再現
assert status == 200
def test_headers():
headers = build_headers()
assert headers == {"Authorization": "Bearer wrong"}
1. オプションなし:比べた dict はそのまま出る
$ export DUMMY_API_KEY=sk-DUMMY-1234567890abcdef
$ python3 -m pytest -q test_api.py
...
E Differing items:
E {'Authorization': 'Bearer sk-DUMMY-1234567890abcdef'} != {'Authorization': 'Bearer wrong'}
test_status(数値だけの比較)ではキーは出ませんでした。--tb=short と --tb=line でも、dict の比較ではキーが1か所出ました。
2. -l を付けると、比較と関係ない変数も出る
$ python3 -m pytest -q -l test_api.py
...
> assert status == 200
E assert 401 == 200
headers = {'Authorization': 'Bearer sk-DUMMY-1234567890abcdef'}
status = 401
-l は pytest --help で「Show locals in tracebacks (disabled by default)」と説明されています。既定では無効ですが、pytest.ini や pyproject.toml の addopts に入れていると毎回有効になります。
3. JUnit XML にも入る
$ python3 -m pytest -q -l test_api.py --junitxml=j.xml
$ grep -c sk-DUMMY j.xml
5
-l なしでも2か所入りました(dict 比較の分)。CI でレポートを保存しているなら、そこにも残ります。
4. 出さない方法(試した範囲)
- 鍵を
__repr__で隠す入れ物に入れる:
class Secret(str):
def __repr__(self): return "'***'"
-l -vv でも {'Authorization': '***'} と表示され、キーは0か所でした。ただし str() や f-string では元の値になります。
-
--tb=no:失敗の詳細を出さないので0か所。原因を追いにくくなります。 -
--no-showlocals:addoptsの-lを打ち消します。dict 比較の1か所は残りました。
確かめるコマンド
grep -rnE -- '(-l\b|--showlocals)' pytest.ini pyproject.toml setup.cfg tox.ini 2>/dev/null
試してみる
コードとログの点検用に ai-secret-check を作っています。今回の pytest の設定はまだ点検対象ではありません。
まとめ
- pytest の失敗ログには、比べた値がそのまま出る
-
-lは関係ない変数まで出す。addoptsに入っていないか確かめる - 鍵は表示用に隠した入れ物に入れるか、ヘッダーそのものを比べるテストを避ける(ヘッダー名や長さを比べる、など)