PR

E2Eテストが見つけてClaude Codeが直した6つのバグ|Streamlitのkey付きウィジェットの罠

(研究者向け)ローカルAI構築キット
記事内に広告が含まれています。

「全選択ボタンを押すとチェックが全部ONに見えるのに、検索対象は0件」。

こんなバグ、人の手のテストでは気づきにくいものです。
この記事では、ローカルAI構築キットのE2Eテスト自動化プロンプトが検知し、Claude Codeが自動修正した6件の実バグを、症状・原因・修正の順に解説します。Streamlitのkey付きウィジェットの罠がほとんどで、Streamlitでツールを作る人全員に関係があります。

E2Eテストが見つけてClaude Codeが直した6つのバグ|Streamlitのkey付きウィジェットの罠

前の記事では、ローカルAI構築キットのE2Eテストが7シナリオ・79チェックを全自動で通したことを書きました。
この記事は、その裏側です。テストが最初から全部通ったわけではなく、6件のバグが検知され、Claude Codeが修正し、再実行して通ったという過程がありました。

6件とも、Streamlitでツールを作ったことがある人なら身に覚えがあるはずの内容です。

1. 自動バグ修正ループとは何をしているのか

キットのテスト自動化プロンプトには、こう書いてあります。

テスト実行中に例外、UI要素の未検出、Streamlitのエラー画面(Traceback表示)を検知した場合、Claude Code自身が該当コード(app.py等)の原因箇所を特定して修正し、テストが全てパスするまで再実行すること。

つまり、テストを書く、走らせる、落ちる、原因を探す、直す、また走らせる、を人の代わりに回します。
修正内容は test_automation_bugfixes.json に症状・原因・修正として記録され、テストレポートの付録に自動で載ります。

2. バグ1:全選択・全解除を押してもチェックボックスが変わらない

症状: 「全解除」を押しても画面上は4件すべてにチェックが付いたまま。
一方で「参照対象: 0 / 4 件」と表示される。見た目は全件ON、実際のRAG検索は0件、という不整合です。

検知: テスト3。全解除後に各チェックボックスの状態を読み取り、全部FalseであることをOFF側から確認していたため発覚しました。

原因: Streamlitのkey付き st.checkbox は、value= 引数よりウィジェット自身の状態を優先します。ボタンはRAG用の選択状態(session_stateの辞書)だけを書き換えていて、チェックボックス側のsession_stateを触っていませんでした。

修正: ボタン押下時に、チェックボックスのkey(chk_{project_id}_{filename})のsession_stateも同時に書き換える。チェックボックスは value= を使わず、未初期化のときだけ選択状態から初期値を入れるようにしました。

3. バグ2:プロジェクトを切り替えても名称変更欄に前の名前が残る

症状: 新しいプロジェクトを作ったのに、「名称変更」欄には前のプロジェクト名が表示されたまま。

なぜ危険か: そのまま「名称を更新」を押すと、別プロジェクトの名前で上書きするか、UNIQUE制約違反になります。

原因: st.text_input のkeyが rename_project_input で固定だったため、value=cur['name'] が無視されていました。

修正: keyをプロジェクトIDごとに分ける(rename_project_input_{id})。切り替えるたびに別のウィジェットとして作り直されるので、前の値が残りません。

4. バグ3:新規プロジェクトの最初のチャットが履歴に出ない

症状: 新規プロジェクトで「新しいチャット」を押すと、スレッド数が0から2に増える。1件目が見えないまま会話が始まっていました。

原因: スレッドの自動作成がメイン領域(サイドバー描画の後)で行われていたため、その回の描画ではサイドバーの履歴一覧が空のままでした。

修正: サイドバーで履歴を読み込んだ時点でスレッドが0件なら、その場で作成して一覧に含めるようにしました。

5. バグ4:編集ボタンの絵文字が空欄で表示される

症状: スレッド名の変更とプロンプト編集の「✏️」ボタンが、幅26pxの細い列の中で空欄になる。

検知: テスト2のスクリーンショットとDOM調査。人が見れば一瞬で分かるタイプのバグですが、「要素は存在するのに描画されない」は自動テストでも拾えました。

修正: ゴミ箱ボタンと同じく、Streamlit同梱のマテリアルアイコン(icon=":material/edit:")に変更。外部通信は発生しません。

6. バグ5:プロンプト追加後にフォームがクリアされない

症状: プロンプトを追加しても、タイトル欄に今入力した内容が残る。さらに、別のプロンプトの編集ボタンを押しても、その内容がフォームに読み込まれない。

原因: バグ2と同じ構造です。タイトル・カテゴリ・本文の入力欄が固定keyだったため、value=editing[...] が無視され、前回の入力値が表示され続けていました。

修正: keyに編集対象のIDと「nonce(通し番号)」を含め、保存・削除・取消のたびにnonceを増やしてフォームを作り直すようにしました。

7. バグ6:削除確認チェックが残り、次のプロジェクトを1クリックで消せてしまう

症状: プロジェクトを削除した後も「削除を確認」チェックがONのまま残り、削除ボタンが有効なまま。削除後は先頭のプロジェクトに切り替わるので、誤クリック1回でそれも消えます。

検知: テスト2の、修正後2回目の実行。1回目の修正で別の問題を直した後に、再実行で見つかりました。ループを回す意味がここにあります。

原因: 削除処理がボタンの戻り値の判定内にあり、その時点では確認チェックボックスが描画済みで状態を戻せませんでした。

修正: 削除処理を on_click コールバックに移し、コールバック内で確認チェックをFalseに戻す。コールバックは再描画の前に動くので、チェックを外せます。

8. 共通する教訓:key付きウィジェットは value= より自分の状態を優先する

6件中3件(バグ1、2、5)が同じ原因です。Streamlitでは、keyを付けたウィジェットは自分のsession_stateを持ち、value= で渡した初期値は「まだ状態がないとき」しか効きません。対処は2つしかありません。

  • 変更したいときは、ウィジェットのkeyのsession_stateを直接書き換える(描画前に)
  • または、対象が変わるたびにkey自体を変えて、別のウィジェットとして作り直す

もう1つの教訓は、削除のような取り消せない操作は on_click コールバックで処理することです。描画後に状態を戻そうとしても手遅れになります。

9. 修正記録はJSONで残り、レポートの付録になる

6件の修正は test_automation_bugfixes.json に「対象ファイル・検知テスト・原因・修正」の形で残り、次回以降のテストレポートの付録Dとして自動で載ります。自分でテストと修正を行った場合は、このファイルを書き換えるか、Claude Codeに更新を依頼するだけです。

キットには、この修正済みのアプリ本体、テストランナー、修正記録のすべてが参照用コードとして入っています。Streamlitで社内ツールを作る予定があるなら、修正記録だけでも読む価値があると思います。

コメント

タイトルとURLをコピーしました