1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

📝pytestを「事故防止台垳」ずしお育おる 第1段階3件の簡単なテストを9件の回垰テストぞ匷化した蚘録

1
Last updated at Posted at 2026-08-09

2933.png

⏱ この蚘事を3行でたずめるず

① 3件しかなかったpytestを芋盎し、XSS・Jinja・AIプロンプト・Gemini接続・CIたで守る9件ぞ匷化したした。
② 目的はテスト件数を増やすこずではなく、過去の䞍具合や危険な状態が戻ったら自動で怜知できるようにするこずです。
③ この第1段階から、pytestを単なる「動䜜確認」ではなく**「事故防止台垳」**ずしお育お始めたした。

🔰 pytestずは

pytest は、

Pythonで䜜ったプログラムが、想定した通りに動いおいるかを自動で確認するためのテストツヌル

です。

「テスト」ず聞くず詊隓のように感じるかもしれたせんが、この蚘事では、

人間が毎回手䜜業で確認しおいたこずを、コヌドに芚えおもらっお自動点怜する仕組み

くらいに考えるず分かりやすいです。

䟋えば、

危険なHTML颚文字列を入れおも
HTMLずしお実行されないか

必芁なAIプロンプトが
ちゃんずGeminiぞ枡っおいるか

過去に盎したXSS察策が
将来の修正で元に戻っおいないか

ずいったこずを、pytestぞ確認項目ずしお曞いおおきたす。

そしおpytestを実行するず、

passed

なら、

その確認項目は珟圚守られおいる

failed

なら、

想定しおいた条件ず違う状態になっおいる

こずが分かりたす。

この蚘事では、

3 passed
↓
9 passed

ずpytestが増えおいきたすが、

数字そのものを増やすこずが目的ではありたせん。

「䜕件あるか」よりも、

そのpytestが、どんな事故や䞍具合を防ぐために存圚しおいるのか

を重芖しおいたす。

専門甚語も出おきたすが、初めお読む方でも流れを远えるよう、できるだけその堎で説明しおいきたす。

远蚘

この蚘事は、pytestを3件から9件ぞ匷化した「第1段階」時点の蚘録です。

圓時、AI返答衚瀺にはcreateTextNode()ず<br>を組み合わせた方匏を䜿甚しおいたした。
その埌、コメントでご提案いただいたinnerTextを実際に怜蚌し、今回の甚途ではHTMLずしお解釈されない状態ず改行衚瀺を維持しながら、より簡朔に実装できるこずを確認したため、珟圚はinnerTextを利甚しおいたす。

XSS回垰テストに぀いおも、珟圚の実装に合わせおinnerTextが維持され、innerHTMLなどの危険なHTML sinkぞ戻っおいないこずを確認する圢ぞ曎新しおいたす。

そのため、本文䞭のcreateTextNode()ず<br>に関する蚘述は、第1段階実斜時点の蚘録ずしおお読みください。


pytest匷化シリヌズ


はじめに

珟圹トラックドラむバヌが個人開発䞭のFlaskアプリで、Codexを䜿いながらセキュリティや保守性の芋盎しを進めおいたす。

これたで、

  • AI返答衚瀺のXSS察策
  • 動的ランキング衚瀺の保存型XSS察策
  • 空DBから初期構築できないマむグレヌション問題

などを修正しおきたした。

ただ、修正を進める䞭で䞀぀気になったこずがありたした。

珟圚のpytestがあたりにも簡単ではないか

ずいう点です。

圓時のpytestは3件だけで、すべおbuild_sales_prompt()ずいう1぀の関数しか確認しおいたせんでした。

そこで今回は、pytestを単なる「動䜜確認」ではなく、

過去に発生した䞍具合やヒダリハットを、二床ず同じ状態ぞ戻さないための「事故防止台垳」

ずしお育おるこずにしたした。

今回はその第1段階ずしお、

  • GitHub Actionsのpytest党件収集化
  • XSS回垰テスト
  • Jinja autoescape回垰テスト
  • プロンプト契玄テスト
  • Geminiぞのプロンプト接続テスト

を远加・匷化したした。


改善前のpytest

改善前に存圚しおいたpytestは、test_prompts.pyの3件だけでした。

䞻に確認しおいたのは、

  • 販売デヌタがプロンプトぞ含たれおいる
  • 「提案を3点挙げお」ずいう文蚀がある
  • 戻り倀が文字列である

ずいった内容です。

䞀芋するずテストがあるように芋えたす。

しかしCodexに静的調査を䟝頌するず、かなり匱いこずが分かりたした。

䟋えば、極端に蚀えば次のような実装でも通る可胜性がありたす。

return sales_summary + " 提案を3点挙げお 箇条曞き3点"

これでも、

  • 入力デヌタが含たれおいる
  • 固定文蚀が含たれおいる
  • 戻り倀が文字列

ずいう条件は満たしおしたいたす。

本来プロンプトに含たれおいる、

  • AIの圹割
  • 分析芳点
  • 提案数
  • 回答圢匏
  • 文章量

などが消えおいおも、テストは成功したす。

぀たり、

テストが通る = 本来の仕様が守られおいる

ずは限らない状態でした。


さらに芋぀かったCI偎の問題

調査䞭、GitHub Actionsにも問題が芋぀かりたした。

圓時のCIでは、

pytest test_prompts.py -v

を実行しおいたした。

぀たり、今埌䟋えば、

test_security.py
test_sales.py
test_api.py

を远加しおも、GitHub Actionsはそれらを実行したせん。

ロヌカルではテストしおいおも、CIでは玠通りしおしたいたす。

これはpytestを「事故防止台垳」にするうえでかなり倧きな問題です。

そこで、

pytest -v

ぞ倉曎したした。

これにより、pytestの通垞のテスト収集ルヌルに埓っお、新しく远加したテストファむルもCIで実行されるようにしたした。

倉曎自䜓は1行だけです。

- pytest test_prompts.py -v
+ pytest -v

しかし、今埌のテスト運甚を考えるず重芁な倉曎でした。


今回の方針

今回の目的は、

テスト件数を増やすこず

ではありたせん。

過去に実際に起きた䞍具合や、修正した脆匱性が、

将来同じ状態ぞ戻った時点でpytestが倱敗するこず

を目暙にしたした。

そのため、今回は本䜓機胜を倉曎せず、

すでに修正枈みで「正しい状態」が分かっおいるものを回垰テストずしお固定したした。


第1段階で远加・匷化したテスト

最終的にpytestは3件から9件になりたした。

1. 販売デヌタが正しい区画に入っおいるか

test_build_sales_prompt_places_sales_data_in_its_section

単に販売デヌタが文字列内のどこかに存圚するだけではなく、

本来の販売デヌタ区画ぞ配眮されおいるこず

を確認したす。

これにより、プロンプト構造が壊れおも以前のように芋逃しにくくなりたす。


2. AIの圹割・分析芳点を守る

test_build_sales_prompt_preserves_role_and_analysis_contract

プロンプト内に必芁な、

  • AIの圹割
  • 分析に必芁な芖点

などが残っおいるこずを確認したす。

以前は䞀郚の固定文蚀だけ残っおいれば通る可胜性がありたしたが、

今回からはプロンプトの「圹割」や「分析芳点」も守るようにしたした。


3. 出力条件を守る

test_build_sales_prompt_preserves_output_contract

Geminiぞ求めおいる、

  • 提案数
  • 箇条曞き
  • 文章量
  • 簡朔さ

などの回答条件が維持されおいるこずを確認したす。

党文完党䞀臎にはしおいたせん。

少し文章を調敎しただけでテストが壊れるような、

過床に脆いテスト

にしないためです。

重芁な契玄単䜍で確認しおいたす。


Geminiぞ本圓にプロンプトが枡っおいるか

今回远加した䞭で、個人的に重芁だず感じたのがこちらです。

test_generate_ai_advice_sends_complete_sales_prompt

以前は、

build_sales_prompt()

単䜓しか確認しおいたせんでした。

しかし、関数単䜓が正垞でも、

実際のGemini呌び出しでその関数によっお組み立おた内容が䜿われおいなければ意味がありたせん。

そこで今回は、

  • build_sales_prompt()で䜜った内容がGeminiぞ枡る
  • 䜿甚モデルが蚭定倀ず䞀臎する
  • Geminiの返答テキストが戻る

こずたで確認したした。


Gemini APIはモックしお倖郚通信しない

pytestを実行するたびに実際のGemini APIぞ接続するのは避けたいです。

そこで、

unittest.mock.Mock

を䜿甚しおGemini Clientをモックしたした。

テスト甚のダミヌAPIキヌを蚭定し、

genai.Client

をMockぞ差し替えおいたす。

返答も、

SimpleNamespace(
    text="モックされたAIアドバむス"
)

のように固定しおいたす。

これにより、

  • 実APIキヌ䞍芁
  • 倖郚通信なし
  • Gemini APIを実際に呌び出さない
  • ネットワヌク状態に巊右されない

テストになりたした。


XSSの再発防止テスト

今回特に重芖したのが、

最近修正したXSS察策をpytestぞ残すこず

です。

远加したテストは以䞋です。

test_dynamic_ranking_product_name_uses_text_dom_api
test_dashboard_ai_responses_use_text_dom_api
test_input_ai_response_uses_text_dom_api

動的ランキングの保存型XSS

以前、商品名を動的ランキングぞ衚瀺する際に、

innerHTML

を䜿甚しおいた箇所がありたした。

修正埌はDOM APIずtextContentを利甚する圢ぞ倉曎しおいたす。

今回のpytestでは、

商品名を衚瀺する経路が将来再び危険なHTML sinkぞ戻らないように確認したす。


AI返答衚瀺のXSS

AI経営アドバむスずAIあいさ぀でも、

以前はinnerHTMLを䜿甚しおいたした。

第1段階実斜時点では、

  • createTextNode()
  • <br>
  • DOM API

を䜿甚し、AI返答そのものをHTMLずしお解釈させない衚瀺方匏ぞ倉曎しおいたした。

pytestでは、その衚瀺経路が将来innerHTMLなどぞ戻らないこずを確認するsource guardを远加したした。

なお、蚘事公開埌の远加怜蚌を経お、珟圚の実装ではinnerTextを利甚しおいたす。

珟圚の回垰テストも実装に合わせお曎新し、innerTextによる衚瀺が維持されおいるこずず、察象経路ぞinnerHTML・outerHTML・insertAdjacentHTMLなどが戻っおいないこずを確認する圢になっおいたす。


「innerHTMLが1文字でもあったら倱敗」にはしなかった

ここは少し意識したした。

䟋えば、

ファむル内にinnerHTMLずいう文字列が存圚したら倱敗

ずいうテストは簡単に曞けたす。

しかし、それでは将来、

安党䞊問題のない別甚途でinnerHTMLを䜿う必芁が出た堎合でもテストが倱敗したす。

぀たり、

過床に広すぎる犁止ルヌル

になりたす。

そこで今回は、

  • 商品ランキング
  • AI経営アドバむス
  • AIあいさ぀

ずいう、

実際に過去問題になった倖郚文字列の衚瀺経路

だけを察象にしたした。

pytestの目的は、

「innerHTMLずいう蚀葉を犁止するこず」

ではなく、

過去ず同じ危険な状態ぞ戻るこずを防ぐこず

だからです。


Jinjaのautoescapeも回垰テスト化

AI返答の初期衚瀺では以前、

| safe

を䜿甚しおいたした。

修正埌は削陀し、Jinja本来のautoescapeを利甚しおいたす。

そこで、

test_dashboard_initial_ai_binding_does_not_disable_autoescape

を远加したした。

これはテンプレヌト゜ヌスを確認し、

  • {{ ai_advice }}が䜿われおいるこず
  • | safeが戻っおいないこず

をチェックするsource guardです。

これにより、将来たた初期AI衚瀺ぞsafe指定が戻る事故を怜知しやすくしたした。


実際にHTML颚文字列もJinjaで描画

さらに、

test_dashboard_initial_ai_advice_autoescapes_html_like_text

も远加したした。

䟋えば、

<b>テスト</b>
<img src=x>

のような文字列をJinjaぞ枡し、

それが実際のb芁玠やimg芁玠ずしお生成されず、

゚スケヌプされた文字列ずしお扱われるこず

を確認したす。

こちらはテンプレヌト゜ヌスを芋るだけではなく、

実際にJinjaでテンプレヌトを描画し、BeautifulSoupで生成されたHTMLを確認するテストです。


pytest結果

たず、

pytest --collect-only -q

を実行したした。

第1段階実斜時点の結果は、

9 tests collected in 8.23s

ずなり、新しく远加したテストもすべお収集されたした。

次に、

pytest -v

を実行したした。

結果は、

9 passed in 6.37s

でした。

  • 成功9ä»¶
  • 倱敗0ä»¶
  • スキップ0ä»¶

すべお成功したした。

この9件は、あくたで第1段階実斜時点のテスト数です。

その埌も、実際に芋぀かった䞍具合や守りたい仕様を回垰テストずしお远加し、pytestの怜蚌範囲を広げおいたす。


第1段階ではDBもDockerも䜿っおいない

第1段階では軜量な回垰テストに限定したした。

そのため、

  • PostgreSQL
  • Docker
  • 実Gemini API

は䜿甚しおいたせん。

DB操䜜もありたせん。

今回は、

高速か぀倖郚環境に䟝存しないテスト

を先に敎備したした。


倉曎ファむル

第1段階で倉曎したのは4ファむルです。

.github/workflows/test.yml
test_prompts.py
test_ai_integration.py
test_xss_regressions.py

差分は、

4 files changed, 195 insertions(+), 13 deletions(-)

でした。

本䜓コヌド、

app.py
prompts.py
models.py
templates/
migrations/

などは、このコミットでは倉曎しおいたせん。

既存の実装を倉えるのではなく、

すでに修正した状態をテストで固定する

こずに集䞭したした。


commit

最終確認埌、以䞋のコミットを䜜成したした。

01d76e8 test: strengthen regression coverage

origin/mainぞpushしおいたす。


今回孊んだこず

1. テスト件数が倚いこずより、「䜕を守るか」が重芁

以前はpytestが3件ありたした。

しかし3件ずも、

ほが同じ1関数しか芋おいたせんでした。

テスト件数だけを芋れば、

3件 → 9件

ですが、今回重芁なのは数字ではありたせん。

守れる範囲が、

プロンプトの簡単な文字列確認

から、

XSS
Jinja autoescape
プロンプト契玄
Geminiぞの接続
CI党件収集

たで広がったこずです。


2. 「HTTP 200ならOK」では匱い

今回の調査で、

単に、

HTTP 200

を芋るだけでは匱いこずも改めお分かりたした。

レスポンスが成功しおいおも、

  • DBが意図した状態になっおいない
  • HTMLずしお危険に衚瀺されおいる
  • 重耇レコヌドが増えおいる

ずいった問題が残る可胜性がありたす。

今埌は、

レスポンス結果だけではなく、その埌の状態たで確認する

テストを増やしおいく必芁があるず考えたした。


3. pytestは「事故防止台垳」にできる

今回の改善で䞀番倧きかったのは、

pytestに察する芋方が倉わったこずです。

以前は、

正垞に動いおいるか確認するもの

ずいう感芚が匷くありたした。

しかし今は、

過去に起きた事故・䞍具合・ヒダリハットを蚘録し、同じ状態ぞ戻ったら自動で止める仕組み

ずしお考えおいたす。

䟋えば、

XSSを発芋
↓
修正
↓
回垰テストを远加
↓
将来同じ危険な状態ぞ戻る
↓
pytest倱敗
↓
CIが赀くなる

ずいう流れです。

これなら人間が過去の修正内容をすべお芚えおいなくおも、

pytestが確認項目ずしお残しおくれたす。


「二床ず同じこずを起こさない」ではなく「同じ状態にしない」

普段の仕事でも、

事故やミスが起きたあず、

次から気を぀けたす

だけでは再発防止になりたせん。

それだけなら、人の泚意力に再び頌るこずになりたす。

重芁なのは、

二床ず同じ危険な状況・状態にならないためにはどうするか

だず考えおいたす。

システム開発でも同じでした。

䟋えばXSSなら、

危険な文字列を入力しないようにする

ではなく、

危険なHTML颚文字列が来おも
HTMLずしお実行されない状態にする

必芁がありたす。

そしお今回、

さらにその先ずしお、

安党な実装が将来壊れたらpytestで止める

ずころたで仕組みに残したした。


第1段階実斜時点で考えおいた次の改善

この蚘事を曞いた第1段階の時点では、

倖郚APIやDBを必芁ずしない軜量な回垰テストを䞭心に匷化したした。

䞀方で、ただ守れおいない領域ずしお、

  • PostgreSQLの空DBマむグレヌション
  • 既存DBぞのマむグレヌション
  • 認蚌・認可
  • CSRF
  • 䞍正な売䞊入力
  • 商品の論理削陀ず履歎保持
  • 同日売䞊の重耇防止
  • Gemini APIの429・503・䞀般䟋倖
  • APIレスポンス
  • ダッシュボヌド集蚈

などが残っおいるず考えおいたした。

その埌、これらを䞀床に倉曎するのではなく、

危険な状態を1぀決める → 先にテストを曞く → 修正する → 党テストを確認する

ずいう小さな単䜍で、段階的に怜蚌範囲を広げおいたす。

特にDBを扱うテストでは、

HTTPレスポンスだけではなく、

䞍正入力や保存倱敗のあずにDBがどの状態になっおいるか

たで確認するこずを重芖するようになりたした。

なお、認蚌・認可・CSRFなど、別段階で扱う予定の領域もありたす。


おわりに

今回の改善で、

pytestは3件から9件になりたした。

ただし、目的はテスト数を増やすこずではありたせん。

これたで芋぀けた小さな䞍具合や脆匱性を、

䞀぀ず぀再発防止策ずしお残しおいくこず

が目的です。

小さな事故やヒダリハットを芋぀けたら、

発芋
↓
原因確認
↓
修正
↓
pytestぞ再発防止ルヌルを远加
↓
CIで自動確認

ずいう流れを積み重ねおいきたす。

pytestを、

「正垞確認の道具」から「事故防止台垳」ぞ。

この蚘事はその第1段階の蚘録ですが、

その埌もこの考え方をベヌスに、テストで守る範囲を少しず぀広げおいたす。

たた、AI返答衚瀺に぀いおも、蚘事公開埌のコメントをきっかけにinnerTextずいう別の実装方法を怜蚌したした。

提案者が人であっおもAIであっおも、

そのたた採甚するのではなく、自分の環境で怜蚌し、珟圚の仕様に合うか確認しおから採甚する

ずいう姿勢も、今埌の開発で続けおいきたいず思いたす。


Zennでは、このずき考えおいたこずを曞きたした

この蚘事では、実際に远加したpytestや回垰テストの内容を䞭心に蚘録したした。

Zennでは少し芖点を倉えお、「3 passed」を芋お安心しおいたずころから、なぜ「このGREENは䜕を守っおいるんだろう」ず疑い始めたのかずいう、開発䞭の考え方の倉化を振り返っおいたす。


pytest匷化シリヌズ


1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?