<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>E2Eテスト</title><link>https://blog.ast.moe/tags/e2e%E3%83%86%E3%82%B9%E3%83%88/</link><description>Recent posts from 五里霧中</description><generator>Hugo</generator><item><title>AIエージェントの開発を「完成」に近づけるために、先にE2Eテストを書くのはどうか</title><link>https://blog.ast.moe/blog/2026-09-28-2/</link><pubDate>Mon, 28 Sep 2026 12:50:00 +0900</pubDate><guid>https://blog.ast.moe/blog/2026-09-28-2/</guid><description>&lt;p&gt;AIエージェントにコードを書かせていると、問題は解決しているのに、作りたいものに近づいている感じがしないことがある。&lt;/p&gt;
&lt;p&gt;実装が進む。レビューすると問題が見つかる。修正すると、別の問題が見つかる。設計上の懸念が提示され、抽象化を見直し、テストを追加し、そのテストを通すために実装を修正する。個々の作業には理由があり、指摘されている問題も、それなりにもっともらしい。&lt;/p&gt;
&lt;p&gt;しかし、しばらく経ってから実際に使おうとすると、最初にやりたかった操作がまだできない。&lt;/p&gt;
&lt;p&gt;自分は基本的に、末端の機能から順番に作っていく。小さな処理を実装し、それを組み合わせ、上位の機能を作る。内部のデータ構造や処理の流れから考えるほうが慣れている。一方で、その作り方だと、UI側から見たときに何が足りないのか、どこが使いにくいのかが分かるのは後になりやすい。&lt;/p&gt;
&lt;p&gt;では、最初からUIを作ればいいのかというと、それも少し怖い。画面に都合のよいデータ構造を先に決めてしまい、裏側がそれを成立させるための無理を引き受けることになりそうだからだ。&lt;/p&gt;
&lt;p&gt;そこで考えたのが、裏側をモックにしてUIを動かしつつ、先にE2Eテストで「作りたいもの」を示しておき、内部は内部で作りながら、両方から擦り寄っていく進め方である。&lt;/p&gt;
&lt;p&gt;ただ、考えていくと、本当に欲しいのはUI先行の開発手順ではない気がしてきた。&lt;/p&gt;
&lt;p&gt;欲しいのは、AIエージェントが問題を解き続けるだけでなく、今回の開発を完成に向けて収束させるための仕組みなのだと思う。&lt;/p&gt;
&lt;p&gt;この記事は、そのためにE2Eテストを使えないか、という仮説を整理するものだ。導入して生産性が何倍になったという実証記事ではない。普段の作り方で感じている違和感から出発し、どういう進め方なら改善できそうか、どこで失敗しそうかまで考えてみたい。&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.ast.moe/blog/2026-09-25-2/"&gt;以前の記事&lt;/a&gt;では、AI Codingで増える実装をどう検証するかを書いた。今回は、検証を増やすだけでなく、そもそも今回の作業をどこで終えるのかを考える。&lt;/p&gt;
&lt;h2 id="部品が増えることとできることが増えることは違う"&gt;部品が増えることと、できることが増えることは違う&lt;/h2&gt;
&lt;p&gt;末端から作る進め方には、自分にとって明確な利点がある。&lt;/p&gt;
&lt;p&gt;例えばファイルを処理するアプリケーションなら、まず入力形式を読み取る処理を作り、変換処理を作り、出力する処理を作る。それぞれの入力と出力を確認しながら進められるし、計算量やメモリの使い方も考えやすい。UIに引っ張られる前に、内部で守りたい条件を整理できる。&lt;/p&gt;
&lt;p&gt;この作り方を捨てたいわけではない。問題は、それだけでは利用者の操作が成立したことにならない点にある。&lt;/p&gt;
&lt;p&gt;入力形式を読めても、画面からファイルを渡せないかもしれない。変換処理が完成していても、処理中に何が起きているのか分からないかもしれない。出力できても、保存先が分からなければ使う側は困る。&lt;/p&gt;
&lt;p&gt;部品ごとの完成と、利用者が目的を達成できることの間には、まだ距離がある。&lt;/p&gt;
&lt;p&gt;自分で全部書いている場合、その距離を頭の中で補いながら進めているのだと思う。「これは後でこの画面につなぐ」「ここは最低限動けば次へ進む」といった判断が、明示的な仕様にならないまま作業に混ざっている。&lt;/p&gt;
&lt;p&gt;その状態で実装だけをエージェントに渡すと、頭の中に残っていた判断まで伝わったことにはならない。部品を詳しく説明できたとしても、どこまで作れば今回使えるのか、何を後回しにしてよいのかは別の話になる。&lt;/p&gt;
&lt;p&gt;ここで必要なのは、内部設計をやめることではなく、内部の進捗とは別に、利用者の側から進捗を測る基準を置くことではないか。&lt;/p&gt;
&lt;h2 id="問題を見つける能力だけでは開発は終わらない"&gt;問題を見つける能力だけでは、開発は終わらない&lt;/h2&gt;
&lt;p&gt;エージェントに「問題がないか確認して」と頼むと、追加で考えるべきことが次々に出てくる。自分の使い方では、それが終わりの見えない作業になりやすい。&lt;/p&gt;
&lt;p&gt;ここで、すべての指摘が間違っていると言いたいわけではない。むしろ、正しい指摘であっても、今それを直すべきかは別に判断する必要がある。&lt;/p&gt;
&lt;p&gt;「将来、別の保存方式を追加しにくい」という指摘は、将来その予定があるなら重要だろう。「並列実行を増やすと状態管理が複雑になる」という指摘も、並列実行が必要なら考えたい。しかし、今作りたいのが一つのファイルを処理する小さな道具なら、その判断は変わる。&lt;/p&gt;
&lt;p&gt;発見された問題を、そのまま現在のタスクへ追加していくと、開発中に終点が動き続けることになる。直した分だけ新しい仕事が追加されるなら、作業量が増えても、残りが減った感じはしない。&lt;/p&gt;
&lt;p&gt;逆に、部品のテストが通っただけで「完成」と言われても困る。終わらない問題探しと、早すぎる完成宣言は反対のように見えるが、どちらも「何をもって完成とするか」が実際の操作と結びついていない、という見方ができる。&lt;/p&gt;
&lt;p&gt;Anthropicの長時間動作するエージェントに関する報告でも、機能を十分に検証せず完成扱いすることや、単体テストなどは行っていても端から端まで機能していないことが課題として挙げられている。ただし、これは同社の特定の実験についての報告であり、自分が感じている「問題を提示され続ける」現象まで証明するものではない。&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;自分の困り方を考えるうえで重要なのは、エージェントの能力をひとまとめに評価することではない。問題の発見、作業の選択、完成の判定を、同じ曖昧な会話の中で処理していることを見直したい。&lt;/p&gt;
&lt;h2 id="仮説e2eテストを完成条件の表現として先に置く"&gt;仮説：E2Eテストを、完成条件の表現として先に置く&lt;/h2&gt;
&lt;p&gt;そこで、実装前に「何ができたら今回の目標を達成したのか」を、利用者の操作と結果で書いておく。&lt;/p&gt;
&lt;p&gt;そのシナリオを自動実行できる形にし、実装の進捗をそこへ結びつける。自分が考えているE2E先行は、そういう使い方である。&lt;/p&gt;
&lt;p&gt;通常の回帰テストとして、「今まで動いていたものが壊れていないか」を確認する役割もある。しかし、それより前に「これから何を動くようにするのか」を示す役割を持たせたい。&lt;/p&gt;
&lt;p&gt;例えば、「ファイル変換機能を実装する」では、入力、変換、保存、UIのどこまで含むのかが曖昧だ。それを「利用者がファイルを選び、条件を指定し、変換した結果を保存して使える」に変える。&lt;/p&gt;
&lt;p&gt;すると、変換関数だけ完成しても、保存できなければ未達だと分かる。一方、その操作が成立し、合意した品質条件も満たしたなら、汎用的なプラグイン機構が未実装でも、今回は完成としてよいかもしれない。&lt;/p&gt;
&lt;p&gt;これは、テストがあれば何でも解決するという話ではない。どのテストを、どの範囲の完成判定として使うかを先に合意する、という話である。&lt;/p&gt;
&lt;p&gt;もちろん、例から期待する振る舞いを定め、実行可能な仕様として実装を導く考え方自体は新しくない。CucumberのBDDの説明でも、具体例について認識を合わせ、それを自動化可能な形にし、実装へつなげる流れが示されている。&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;自分がここで考えたいのは、それをAIエージェントの作業の選択と終了条件に使うことだ。新しい開発手法を発明したというより、既存の考え方を、今困っている場所に適用してみたい。&lt;/p&gt;
&lt;p&gt;自分がここで区別したいのは、何を満たせば受け入れるかと、システムのどの範囲を通して検証するかである。この記事では、利用者の主要な受け入れ条件を、実接続のE2Eでも確認する構成を考えている。すべての受け入れ条件をUI操作へ置き換える、という意味ではない。&lt;/p&gt;
&lt;h2 id="先に決めるのは画面の完成形ではない"&gt;先に決めるのは、画面の完成形ではない&lt;/h2&gt;
&lt;p&gt;ここで先に決めたいのは、ボタンの配置やコンポーネントの分割ではない。利用者が何をして、最終的に何を得られるのかである。&lt;/p&gt;
&lt;p&gt;以降は説明のために、画像を指定した幅へ縮小して保存する、小さなアプリケーションを仮定する。実際にこの方法で開発して検証した事例ではない。&lt;/p&gt;
&lt;p&gt;最初のシナリオは、例えば次のように書ける。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;シナリオ：画像を指定した幅へ縮小して保存できる
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;前提：幅1200、高さ800ピクセルの有効な画像ファイルがある。
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;操作：利用者が画像を選択し、出力幅に600を指定して処理する。
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;結果：出力画像を保存できる。
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;確認：保存した画像は幅600、高さ400ピクセルで、入力に対応する画像内容を持つ。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;ここには、DBのテーブル名も、画像バッファの持ち方も、処理スレッド数も出てこない。画面が一枚なのか、ウィザード形式なのかも決めていない。&lt;/p&gt;
&lt;p&gt;一方で、「保存できる」だけで十分か、「入力と同じ画像内容を保つ」とは何を確認するのか、といった曖昧さは残る。これらは実装に入る前後で具体化しなければならない。シナリオを書けば仕様が自動的に完成するわけではなく、曖昧な部分を見つけやすくするために書く。&lt;/p&gt;
&lt;p&gt;例えば最初の対象をPNGに限定すれば、非可逆圧縮の差異をどう扱うかは後へ送れる。画質の細かい評価より、まず既知の模様を持つ画像で、寸法と内容の対応を確かめる、という切り方も考えられる。&lt;/p&gt;
&lt;p&gt;UIテストの操作対象も、DOMの階層や偶然のCSSクラスではなく、利用者が認識するラベルや役割を中心に指定したい。Playwrightも、利用者に見える振る舞いを検証し、内部実装への依存を避ける方針を示している。&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;先に固定するのは、実装の形ではなく、今回提供したい価値の輪郭である。&lt;/p&gt;
&lt;h2 id="今回やらないことも仕様に含める"&gt;「今回やらないこと」も仕様に含める&lt;/h2&gt;
&lt;p&gt;完成条件を先に書こうとして、エージェントへ「必要なE2Eテストを全部考えて」と丸投げしたらどうなるだろう。&lt;/p&gt;
&lt;p&gt;画像を扱うなら、複数ファイル、ドラッグアンドドロップ、履歴、クラウド同期、再実行、キャンセル、形式変換など、考えられる機能はいくらでもある。それらを全部完成条件へ入れてしまえば、問題リストがテストリストへ変わっただけになる。&lt;/p&gt;
&lt;p&gt;だから、今回の範囲は有限にしたい。&lt;/p&gt;
&lt;p&gt;先ほどの例なら、最初は単一のPNGを指定し、幅を変更して保存できればよい、と決める。複数ファイルの一括処理や履歴管理は対象外にする。これはアプリケーションが将来持つ機能を否定しているのではなく、今回の開発で何を完成させるかを区切っている。&lt;/p&gt;
&lt;p&gt;この区切りは、機能だけでなく、扱う入力や利用環境にも必要だろう。「どんな画像でも動く」ではなく、まずどの形式、どの規模を対象とするかを示す。ただし、対象外の入力に対して壊れてよいという意味ではない。受け付けないなら、拒否する振る舞いを決める必要がある。&lt;/p&gt;
&lt;p&gt;そして、実装中に新しい要求が見つかったら、発見したこと自体は記録する。ただし、自動的に今回の範囲へ追加しない。今の目的に不可欠なのか、次の段階でよいのか、別途判断する。&lt;/p&gt;
&lt;p&gt;自分がE2Eで欲しいのは、無限に細かい仕様書ではない。「これが動けば、とりあえず自分が使える」という最小限の到達点を、会話から取り出して残すことだ。&lt;/p&gt;
&lt;p&gt;先に決める範囲が大きすぎるなら、まだ最初の一歩を決められていないのかもしれない。テストを書く前に、作りたいものを一度小さくする必要がある。&lt;/p&gt;
&lt;h2 id="uiと内部は操作と結果で合流させる"&gt;UIと内部は、「操作と結果」で合流させる&lt;/h2&gt;
&lt;p&gt;最初の懸念へ戻る。UI側から作ると、内部のデータ構造が画面の都合に引っ張られないだろうか。&lt;/p&gt;
&lt;p&gt;この点は、先に何を合意するかで変わると思う。UIが使うデータの形を、そのまま内部の保存形式や処理形式として採用するなら、確かに怖い。しかし、利用者が要求する操作と、その結果として観測できる状態を合意するだけなら、内部にはまだ選択の余地がある。&lt;/p&gt;
&lt;p&gt;例えば、処理の開始、状態の取得、結果の取得という境界を置く。概念的には、次の程度でよい。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;startResize(input, options) → jobId
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;getJob(jobId) → 待機中／処理中／成功／失敗
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;getResult(jobId) → 出力画像
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;これはAPI設計を確定した例ではない。実際には入力の渡し方や失敗の表現なども必要だし、同期処理で十分ならジョブという概念すら不要かもしれない。両側が話し合う場所を、まず小さく作るという意味である。&lt;/p&gt;</description></item></channel></rss>