{"componentChunkName":"component---src-templates-post-tsx","path":"/claude-code-test-automation","result":{"data":{"site":{"siteMetadata":{"title":"Linknet Engineers Blog"}},"markdownRemark":{"id":"ed881c14-452f-53f1-adb2-ca4205496f6b","excerpt":"こんにちは。株式会社リンクネット ソリューション事業部の森正です。 弊社では Claude Code を使った開発のなかで、テストコードの作成も Claude Code に任せることで効率化を図っています。 今回は Laravel 1…","html":"<p>こんにちは。株式会社リンクネット ソリューション事業部の森正です。</p>\n<p>弊社では Claude Code を使った開発のなかで、テストコードの作成も Claude Code に任せることで効率化を図っています。</p>\n<p>今回は Laravel 12 で作成したタスク管理アプリを題材に、Unit・Feature・E2E（Playwright）の3層すべてを Claude Code に生成させる流れを紹介します。</p>\n<hr>\n<h2>1. サンプルプロジェクトの概要</h2>\n<p>題材は、Laravel 12 + React + Inertia.js で構成したタスク管理アプリです。</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1280px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 89.25%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAIAAADUsmlHAAAACXBIWXMAAAsTAAALEwEAmpwYAAABXklEQVQ4y5VT226DMAzN///IpP3ItKdp79O2jhW6ckkCJLHjBHZg6t5aqGUEkXDOxbYKIUtMCGK88nQj8jTPkzWpLGmeGWcViJ2nYfTGOnxEEY5Xk1jyJEVBjw/2+cmpYXRl9VNr7ZhAQVJO11NQO+W+l9cXf/gkNTpfFMe66yjFGAXPtCPmGQploT06d6pOjbUjc74t+097npgFqYhYG3MsK2NN8L2spm0mrAFNRczG2OK7tEa7QYNIztvgIgn+KefDV3G01ghZmIn79mhe3RHlQ9DaNG1LzhDRzmIgL8WBCLTP55q85SXiHcgQ2fdDVcHvuh/c3cXDMALc+4AzYodfF9reU9tpgCPRthnju0vziozxPHwVnTZAhnmox5Js0770OXadbtpu2YpVSUp5L22OEX2qm9YHDmEv7bgumbK2f3v/wHhq08M8XLmrWP7dxmYtcpmWNi+8NxO/AfkXjooemh8nhVoAAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <picture>\n          <source\n              srcset=\"/static/460cd97be6f235855b2e67d25b7a18a8/28a80/app-task-list.webp 400w,\n/static/460cd97be6f235855b2e67d25b7a18a8/8d2ea/app-task-list.webp 800w,\n/static/460cd97be6f235855b2e67d25b7a18a8/4b075/app-task-list.webp 1280w\"\n              sizes=\"(max-width: 1280px) 100vw, 1280px\"\n              type=\"image/webp\"\n            />\n          <source\n            srcset=\"/static/460cd97be6f235855b2e67d25b7a18a8/a3397/app-task-list.png 400w,\n/static/460cd97be6f235855b2e67d25b7a18a8/a331c/app-task-list.png 800w,\n/static/460cd97be6f235855b2e67d25b7a18a8/26c69/app-task-list.png 1280w\"\n            sizes=\"(max-width: 1280px) 100vw, 1280px\"\n            type=\"image/png\"\n          />\n          <img\n            class=\"gatsby-resp-image-image\"\n            src=\"/static/460cd97be6f235855b2e67d25b7a18a8/26c69/app-task-list.png\"\n            alt=\"タスク一覧画面\"\n            title=\"タスク一覧画面\"\n            loading=\"lazy\"\n            style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n          />\n        </picture>\n    </span></p>\n<p>タスクの作成・編集はダイアログで行います。</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1280px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAABcUlEQVQoz22SS27CMBiEc5Z2QxPixHagWZBsuAt7VLogvUpZobKh4g7tQQpqq0Ih4SHCI+9OYlop0NHI+iX7s3+PLbFClMJU1IqiyLJ8cyExj1FVWaVCicokMJqm6bqOKYymaT72ek+DwXA4fC6EYjQa9ft9TGGBYXDA11e0qjBJp7RaJUTTmMGxi2XZ09lyd4jjNE2yDEaRZdlm69u2XcAQNwzGeX5y3glo7EJy2HobzxdeOJvvhaffu4UXTN5d7Ctgzrm4gsRo3i3gvH9CAC9cN06SIAzDKIJRJGm6Wq//TgYs0pEYEzABTFS10bBcbxVE8TGIhA/HMIpTz/sPBqOSPDAEiTtjxcfn19bf7/ZHYX93wPne0kNTlzArwj7BWOH7PhJKi5zEMBlnry+bZtPGynMYzwQsf0JKa7V6p3PvOA/drnOy47TbTqt1Z5q3AHihEoycoZymrPghJePXqKr8C/JSYCVxXr8Q2oE5P4d/AKEl/07I7eFnAAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n  ></span>\n  <picture>\n          <source\n              srcset=\"/static/eb3573614aa89fa23657016b54f824fc/28a80/app-create-dialog.webp 400w,\n/static/eb3573614aa89fa23657016b54f824fc/8d2ea/app-create-dialog.webp 800w,\n/static/eb3573614aa89fa23657016b54f824fc/4b075/app-create-dialog.webp 1280w\"\n              sizes=\"(max-width: 1280px) 100vw, 1280px\"\n              type=\"image/webp\"\n            />\n          <source\n            srcset=\"/static/eb3573614aa89fa23657016b54f824fc/a3397/app-create-dialog.png 400w,\n/static/eb3573614aa89fa23657016b54f824fc/a331c/app-create-dialog.png 800w,\n/static/eb3573614aa89fa23657016b54f824fc/26c69/app-create-dialog.png 1280w\"\n            sizes=\"(max-width: 1280px) 100vw, 1280px\"\n            type=\"image/png\"\n          />\n          <img\n            class=\"gatsby-resp-image-image\"\n            src=\"/static/eb3573614aa89fa23657016b54f824fc/26c69/app-create-dialog.png\"\n            alt=\"タスク作成ダイアログ\"\n            title=\"タスク作成ダイアログ\"\n            loading=\"lazy\"\n            style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n          />\n        </picture>\n    </span></p>\n<p>機能は、ユーザー認証（Laravel Breeze）、タスクの作成・参照・更新・削除、ステータス管理（未着手・進行中・完了）とそのフィルタリングを備えています。</p>\n<p>最終的に生成したテストは、以下のような構成になりました。</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">tests/\n├── Unit/                    # Unit テスト\n│   └── Models/TaskTest.php\n├── Feature/                 # Feature テスト\n│   └── Tasks/\n│       ├── TaskCreateTest.php\n│       ├── TaskUpdateTest.php\n│       ├── TaskDeleteTest.php\n│       └── TaskIndexTest.php\n└── e2e/                     # E2Eテスト (Playwright)\n    ├── helpers/auth.ts\n    └── tasks/task-crud.spec.ts</code></pre></div>\n<hr>\n<h2>2. まず CLAUDE.md にテスト方針を書く</h2>\n<p>いきなりテストを書かせる前に、やっておくと良いのが <strong><code class=\"language-text\">CLAUDE.md</code> への方針の明記</strong>です。</p>\n<p>ここにテストの規約を書いておくと、毎回プロンプトで細かく指示しなくても、一貫したスタイルのテストが出てくるようになります。</p>\n<div class=\"gatsby-highlight\" data-language=\"markdown\"><pre class=\"language-markdown\"><code class=\"language-markdown\"><span class=\"token title important\"><span class=\"token punctuation\">#</span> テスト方針</span>\n\n<span class=\"token title important\"><span class=\"token punctuation\">##</span> PHPUnit</span>\n<span class=\"token list punctuation\">-</span> テストクラスには <span class=\"token code keyword\">`use RefreshDatabase`</span> を使用する\n<span class=\"token list punctuation\">-</span> テストメソッドには <span class=\"token code keyword\">`#[Test]`</span> アトリビュートを付与する\n<span class=\"token list punctuation\">-</span> メソッド名は英語のスネークケースで、テスト対象の振る舞いを説明する\n<span class=\"token list punctuation\">-</span> Factory を使用してテストデータを生成する\n<span class=\"token list punctuation\">-</span> 認証が必要なテストは <span class=\"token code keyword\">`actingAs($user)`</span> を使用する\n\n<span class=\"token title important\"><span class=\"token punctuation\">##</span> Playwright (E2E)</span>\n<span class=\"token list punctuation\">-</span> テストファイルは <span class=\"token code keyword\">`tests/e2e/`</span> 配下に配置する\n<span class=\"token list punctuation\">-</span> 要素の特定には <span class=\"token code keyword\">`data-testid`</span> 属性を使用する\n<span class=\"token list punctuation\">-</span> ログインが必要なテストは <span class=\"token code keyword\">`helpers/auth.ts`</span> の <span class=\"token code keyword\">`login()`</span> 関数を使用する\n\n<span class=\"token title important\"><span class=\"token punctuation\">##</span> テスト実行コマンド</span>\n<span class=\"token list punctuation\">-</span> PHPUnit: <span class=\"token code keyword\">`./vendor/bin/sail test`</span>\n<span class=\"token list punctuation\">-</span> Playwright: <span class=\"token code keyword\">`./vendor/bin/sail npx playwright test`</span></code></pre></div>\n<hr>\n<h2>3. Unit テスト ── ロジックの正しさを確かめる</h2>\n<p>まずはモデルの Unit テストです。</p>\n<p>次のように指示します。</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">Task モデルのユニットテストを作成してください。\nリレーション、Enumキャスト、スコープのテストを含めてください。</code></pre></div>\n<p>生成されたのが次のテストです。</p>\n<p>モデルの定義を読み取り、検証すべき内容を自動で抽出しています。</p>\n<div class=\"gatsby-highlight\" data-language=\"php\"><pre class=\"language-php\"><code class=\"language-php\"><span class=\"token php language-php\"><span class=\"token delimiter important\">&lt;?php</span>\n\n<span class=\"token keyword\">namespace</span> <span class=\"token package\">Tests<span class=\"token punctuation\">\\</span>Unit<span class=\"token punctuation\">\\</span>Models</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">use</span> <span class=\"token package\">App<span class=\"token punctuation\">\\</span>Enums<span class=\"token punctuation\">\\</span>TaskStatus</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">use</span> <span class=\"token package\">App<span class=\"token punctuation\">\\</span>Models<span class=\"token punctuation\">\\</span>Task</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">use</span> <span class=\"token package\">App<span class=\"token punctuation\">\\</span>Models<span class=\"token punctuation\">\\</span>User</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">use</span> <span class=\"token package\">Illuminate<span class=\"token punctuation\">\\</span>Foundation<span class=\"token punctuation\">\\</span>Testing<span class=\"token punctuation\">\\</span>RefreshDatabase</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">use</span> <span class=\"token package\">PHPUnit<span class=\"token punctuation\">\\</span>Framework<span class=\"token punctuation\">\\</span>Attributes<span class=\"token punctuation\">\\</span>Test</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">use</span> <span class=\"token package\">Tests<span class=\"token punctuation\">\\</span>TestCase</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">TaskTest</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">TestCase</span>\n<span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">use</span> <span class=\"token package\">RefreshDatabase</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token attribute\"><span class=\"token delimiter punctuation\">#[</span><span class=\"token attribute-content\"><span class=\"token attribute-class-name class-name\">Test</span></span><span class=\"token delimiter punctuation\">]</span></span>\n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">function</span> <span class=\"token function\">it_belongs_to_a_user</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span> <span class=\"token keyword return-type\">void</span>\n    <span class=\"token punctuation\">{</span>\n        <span class=\"token variable\">$task</span> <span class=\"token operator\">=</span> <span class=\"token class-name static-context\">Task</span><span class=\"token operator\">::</span><span class=\"token function\">factory</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">-></span><span class=\"token function\">create</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token variable\">$this</span><span class=\"token operator\">-></span><span class=\"token function\">assertInstanceOf</span><span class=\"token punctuation\">(</span><span class=\"token class-name static-context\">User</span><span class=\"token operator\">::</span><span class=\"token keyword\">class</span><span class=\"token punctuation\">,</span> <span class=\"token variable\">$task</span><span class=\"token operator\">-></span><span class=\"token property\">user</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token attribute\"><span class=\"token delimiter punctuation\">#[</span><span class=\"token attribute-content\"><span class=\"token attribute-class-name class-name\">Test</span></span><span class=\"token delimiter punctuation\">]</span></span>\n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">function</span> <span class=\"token function\">it_casts_status_to_enum</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span> <span class=\"token keyword return-type\">void</span>\n    <span class=\"token punctuation\">{</span>\n        <span class=\"token variable\">$task</span> <span class=\"token operator\">=</span> <span class=\"token class-name static-context\">Task</span><span class=\"token operator\">::</span><span class=\"token function\">factory</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">-></span><span class=\"token function\">todo</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">-></span><span class=\"token function\">create</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token variable\">$this</span><span class=\"token operator\">-></span><span class=\"token function\">assertInstanceOf</span><span class=\"token punctuation\">(</span><span class=\"token class-name static-context\">TaskStatus</span><span class=\"token operator\">::</span><span class=\"token keyword\">class</span><span class=\"token punctuation\">,</span> <span class=\"token variable\">$task</span><span class=\"token operator\">-></span><span class=\"token property\">status</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token variable\">$this</span><span class=\"token operator\">-></span><span class=\"token function\">assertSame</span><span class=\"token punctuation\">(</span><span class=\"token class-name static-context\">TaskStatus</span><span class=\"token operator\">::</span>Todo<span class=\"token punctuation\">,</span> <span class=\"token variable\">$task</span><span class=\"token operator\">-></span><span class=\"token property\">status</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token attribute\"><span class=\"token delimiter punctuation\">#[</span><span class=\"token attribute-content\"><span class=\"token attribute-class-name class-name\">Test</span></span><span class=\"token delimiter punctuation\">]</span></span>\n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">function</span> <span class=\"token function\">scope_status_filters_correctly</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span> <span class=\"token keyword return-type\">void</span>\n    <span class=\"token punctuation\">{</span>\n        <span class=\"token variable\">$user</span> <span class=\"token operator\">=</span> <span class=\"token class-name static-context\">User</span><span class=\"token operator\">::</span><span class=\"token function\">factory</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">-></span><span class=\"token function\">create</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token class-name static-context\">Task</span><span class=\"token operator\">::</span><span class=\"token function\">factory</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">-></span><span class=\"token function\">count</span><span class=\"token punctuation\">(</span><span class=\"token number\">3</span><span class=\"token punctuation\">)</span><span class=\"token operator\">-></span><span class=\"token function\">todo</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">-></span><span class=\"token keyword\">for</span><span class=\"token punctuation\">(</span><span class=\"token variable\">$user</span><span class=\"token punctuation\">)</span><span class=\"token operator\">-></span><span class=\"token function\">create</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token class-name static-context\">Task</span><span class=\"token operator\">::</span><span class=\"token function\">factory</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">-></span><span class=\"token function\">count</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token operator\">-></span><span class=\"token function\">done</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">-></span><span class=\"token keyword\">for</span><span class=\"token punctuation\">(</span><span class=\"token variable\">$user</span><span class=\"token punctuation\">)</span><span class=\"token operator\">-></span><span class=\"token function\">create</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token variable\">$this</span><span class=\"token operator\">-></span><span class=\"token function\">assertCount</span><span class=\"token punctuation\">(</span><span class=\"token number\">3</span><span class=\"token punctuation\">,</span> <span class=\"token class-name static-context\">Task</span><span class=\"token operator\">::</span><span class=\"token function\">status</span><span class=\"token punctuation\">(</span><span class=\"token class-name static-context\">TaskStatus</span><span class=\"token operator\">::</span>Todo<span class=\"token punctuation\">)</span><span class=\"token operator\">-></span><span class=\"token function\">get</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token variable\">$this</span><span class=\"token operator\">-></span><span class=\"token function\">assertCount</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token class-name static-context\">Task</span><span class=\"token operator\">::</span><span class=\"token function\">status</span><span class=\"token punctuation\">(</span><span class=\"token class-name static-context\">TaskStatus</span><span class=\"token operator\">::</span>Done<span class=\"token punctuation\">)</span><span class=\"token operator\">-></span><span class=\"token function\">get</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></span></code></pre></div>\n<p>リレーション・Enumキャスト・カスタムスコープと、検証すべき観点を適切に押さえています。</p>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\">$ ./vendor/bin/sail <span class=\"token builtin class-name\">test</span> tests/Unit/Models/TaskTest.php\n\n   PASS  Tests<span class=\"token punctuation\">\\</span>Unit<span class=\"token punctuation\">\\</span>Models<span class=\"token punctuation\">\\</span>TaskTest\n  ✓ it belongs to a user\n  ✓ it casts status to enum\n  ✓ scope status filters correctly\n\n  Tests:    <span class=\"token number\">3</span> passed <span class=\"token punctuation\">(</span><span class=\"token number\">7</span> assertions<span class=\"token punctuation\">)</span></code></pre></div>\n<hr>\n<h2>4. Feature テスト ── 観点を箇条書きで渡す</h2>\n<p>Feature テストでは、API レベルの動作を確認します。</p>\n<p>ここでのポイントは、<strong>確認したい観点を箇条書きで渡す</strong>ことです。</p>\n<p>これにより、Claude Code が漏れなくテストケースに反映します。</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">TaskController の作成・参照・更新・削除操作に対するフィーチャーテストを作成してください。\n以下の観点を含めてください：\n- 認証チェック\n- 正常系（作成・更新・削除・一覧表示）\n- 認可チェック（他ユーザーのタスクは操作できない）\n- バリデーション\n- ステータスフィルタリング</code></pre></div>\n<p>生成された作成テストの抜粋です。</p>\n<div class=\"gatsby-highlight\" data-language=\"php\"><pre class=\"language-php\"><code class=\"language-php\"><span class=\"token php language-php\"><span class=\"token delimiter important\">&lt;?php</span>\n\n<span class=\"token keyword\">namespace</span> <span class=\"token package\">Tests<span class=\"token punctuation\">\\</span>Feature<span class=\"token punctuation\">\\</span>Tasks</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">use</span> <span class=\"token package\">App<span class=\"token punctuation\">\\</span>Models<span class=\"token punctuation\">\\</span>User</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">use</span> <span class=\"token package\">Illuminate<span class=\"token punctuation\">\\</span>Foundation<span class=\"token punctuation\">\\</span>Testing<span class=\"token punctuation\">\\</span>RefreshDatabase</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">use</span> <span class=\"token package\">PHPUnit<span class=\"token punctuation\">\\</span>Framework<span class=\"token punctuation\">\\</span>Attributes<span class=\"token punctuation\">\\</span>Test</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">use</span> <span class=\"token package\">Tests<span class=\"token punctuation\">\\</span>TestCase</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">TaskCreateTest</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">TestCase</span>\n<span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">use</span> <span class=\"token package\">RefreshDatabase</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token attribute\"><span class=\"token delimiter punctuation\">#[</span><span class=\"token attribute-content\"><span class=\"token attribute-class-name class-name\">Test</span></span><span class=\"token delimiter punctuation\">]</span></span>\n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">function</span> <span class=\"token function\">guest_cannot_access_create_form</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span> <span class=\"token keyword return-type\">void</span>\n    <span class=\"token punctuation\">{</span>\n        <span class=\"token variable\">$this</span><span class=\"token operator\">-></span><span class=\"token function\">get</span><span class=\"token punctuation\">(</span><span class=\"token function\">route</span><span class=\"token punctuation\">(</span><span class=\"token string single-quoted-string\">'tasks.create'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token operator\">-></span><span class=\"token function\">assertRedirect</span><span class=\"token punctuation\">(</span><span class=\"token function\">route</span><span class=\"token punctuation\">(</span><span class=\"token string single-quoted-string\">'login'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token attribute\"><span class=\"token delimiter punctuation\">#[</span><span class=\"token attribute-content\"><span class=\"token attribute-class-name class-name\">Test</span></span><span class=\"token delimiter punctuation\">]</span></span>\n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">function</span> <span class=\"token function\">user_can_create_a_task</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span> <span class=\"token keyword return-type\">void</span>\n    <span class=\"token punctuation\">{</span>\n        <span class=\"token variable\">$user</span> <span class=\"token operator\">=</span> <span class=\"token class-name static-context\">User</span><span class=\"token operator\">::</span><span class=\"token function\">factory</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">-></span><span class=\"token function\">create</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token variable\">$response</span> <span class=\"token operator\">=</span> <span class=\"token variable\">$this</span><span class=\"token operator\">-></span><span class=\"token function\">actingAs</span><span class=\"token punctuation\">(</span><span class=\"token variable\">$user</span><span class=\"token punctuation\">)</span><span class=\"token operator\">-></span><span class=\"token function\">post</span><span class=\"token punctuation\">(</span><span class=\"token function\">route</span><span class=\"token punctuation\">(</span><span class=\"token string single-quoted-string\">'tasks.store'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>\n            <span class=\"token string single-quoted-string\">'title'</span> <span class=\"token operator\">=></span> <span class=\"token string single-quoted-string\">'テストタスク'</span><span class=\"token punctuation\">,</span>\n            <span class=\"token string single-quoted-string\">'description'</span> <span class=\"token operator\">=></span> <span class=\"token string single-quoted-string\">'テストの説明'</span><span class=\"token punctuation\">,</span>\n            <span class=\"token string single-quoted-string\">'status'</span> <span class=\"token operator\">=></span> <span class=\"token string single-quoted-string\">'todo'</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token variable\">$response</span><span class=\"token operator\">-></span><span class=\"token function\">assertRedirect</span><span class=\"token punctuation\">(</span><span class=\"token function\">route</span><span class=\"token punctuation\">(</span><span class=\"token string single-quoted-string\">'tasks.index'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token variable\">$this</span><span class=\"token operator\">-></span><span class=\"token function\">assertDatabaseHas</span><span class=\"token punctuation\">(</span><span class=\"token string single-quoted-string\">'tasks'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>\n            <span class=\"token string single-quoted-string\">'title'</span> <span class=\"token operator\">=></span> <span class=\"token string single-quoted-string\">'テストタスク'</span><span class=\"token punctuation\">,</span>\n            <span class=\"token string single-quoted-string\">'user_id'</span> <span class=\"token operator\">=></span> <span class=\"token variable\">$user</span><span class=\"token operator\">-></span><span class=\"token property\">id</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token attribute\"><span class=\"token delimiter punctuation\">#[</span><span class=\"token attribute-content\"><span class=\"token attribute-class-name class-name\">Test</span></span><span class=\"token delimiter punctuation\">]</span></span>\n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">function</span> <span class=\"token function\">title_is_required</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span> <span class=\"token keyword return-type\">void</span>\n    <span class=\"token punctuation\">{</span>\n        <span class=\"token variable\">$user</span> <span class=\"token operator\">=</span> <span class=\"token class-name static-context\">User</span><span class=\"token operator\">::</span><span class=\"token function\">factory</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">-></span><span class=\"token function\">create</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token variable\">$this</span><span class=\"token operator\">-></span><span class=\"token function\">actingAs</span><span class=\"token punctuation\">(</span><span class=\"token variable\">$user</span><span class=\"token punctuation\">)</span>\n            <span class=\"token operator\">-></span><span class=\"token function\">post</span><span class=\"token punctuation\">(</span><span class=\"token function\">route</span><span class=\"token punctuation\">(</span><span class=\"token string single-quoted-string\">'tasks.store'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token string single-quoted-string\">'title'</span> <span class=\"token operator\">=></span> <span class=\"token string single-quoted-string\">''</span><span class=\"token punctuation\">,</span> <span class=\"token string single-quoted-string\">'status'</span> <span class=\"token operator\">=></span> <span class=\"token string single-quoted-string\">'todo'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n            <span class=\"token operator\">-></span><span class=\"token function\">assertSessionHasErrors</span><span class=\"token punctuation\">(</span><span class=\"token string single-quoted-string\">'title'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></span></code></pre></div>\n<p>Controller のルーティング、FormRequest のバリデーションルール、Policy の認可ロジックまで読み取ったうえで、観点ごとのテストケースを作成しています。</p>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\">$ ./vendor/bin/sail <span class=\"token builtin class-name\">test</span> tests/Feature/Tasks/\n\n   PASS  Tests<span class=\"token punctuation\">\\</span>Feature<span class=\"token punctuation\">\\</span>Tasks<span class=\"token punctuation\">\\</span>TaskCreateTest\n  ✓ guest cannot access create form\n  ✓ user can create a task\n  ✓ title is required\n  ✓ title max length is <span class=\"token number\">255</span>\n  ✓ status must be valid enum value\n  <span class=\"token punctuation\">..</span>.\n\n  Tests:    <span class=\"token number\">17</span> passed <span class=\"token punctuation\">(</span><span class=\"token number\">58</span> assertions<span class=\"token punctuation\">)</span></code></pre></div>\n<hr>\n<h2>5. E2Eテスト（Playwright）</h2>\n<p>E2E には Playwright を使用します。</p>\n<p>まずは環境のセットアップから依頼します。</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">Playwright のセットアップをしてください。\nLaravel Sail 環境で動作するようにしてください。</code></pre></div>\n<p>設定では、テスト前にDBをリセットして再シードする <code class=\"language-text\">globalSetup</code> も生成されます。</p>\n<div class=\"gatsby-highlight\" data-language=\"typescript\"><pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token comment\">// playwright.config.ts</span>\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">default</span> <span class=\"token function\">defineConfig</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    testDir<span class=\"token operator\">:</span> <span class=\"token string\">'./tests/e2e'</span><span class=\"token punctuation\">,</span>\n    workers<span class=\"token operator\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n    use<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        baseURL<span class=\"token operator\">:</span> <span class=\"token string\">'http://localhost'</span><span class=\"token punctuation\">,</span>\n        screenshot<span class=\"token operator\">:</span> <span class=\"token string\">'only-on-failure'</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    globalSetup<span class=\"token operator\">:</span> <span class=\"token string\">'./tests/e2e/global-setup.ts'</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>ログインのような共通処理は、ヘルパーとして切り出して生成されます。</p>\n<div class=\"gatsby-highlight\" data-language=\"typescript\"><pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token comment\">// tests/e2e/helpers/auth.ts</span>\n<span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> Page <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'@playwright/test'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">login</span><span class=\"token punctuation\">(</span>page<span class=\"token operator\">:</span> Page<span class=\"token punctuation\">,</span> email <span class=\"token operator\">=</span> <span class=\"token string\">'test@example.com'</span><span class=\"token punctuation\">,</span> password <span class=\"token operator\">=</span> <span class=\"token string\">'password'</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">await</span> page<span class=\"token punctuation\">.</span><span class=\"token function\">goto</span><span class=\"token punctuation\">(</span><span class=\"token string\">'/login'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">await</span> page<span class=\"token punctuation\">.</span><span class=\"token function\">locator</span><span class=\"token punctuation\">(</span><span class=\"token string\">'#email'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">fill</span><span class=\"token punctuation\">(</span>email<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">await</span> page<span class=\"token punctuation\">.</span><span class=\"token function\">locator</span><span class=\"token punctuation\">(</span><span class=\"token string\">'#password'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">fill</span><span class=\"token punctuation\">(</span>password<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">await</span> page<span class=\"token punctuation\">.</span><span class=\"token function\">getByRole</span><span class=\"token punctuation\">(</span><span class=\"token string\">'button'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> name<span class=\"token operator\">:</span> <span class=\"token string\">'ログイン'</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">click</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">await</span> page<span class=\"token punctuation\">.</span><span class=\"token function\">waitForURL</span><span class=\"token punctuation\">(</span><span class=\"token string\">'**/dashboard'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> timeout<span class=\"token operator\">:</span> <span class=\"token number\">10000</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>そのうえで一連の操作の E2E を依頼します。</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">タスクの作成・参照・更新・削除操作に対する E2E テストを Playwright で作成してください。</code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"typescript\"><pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> test<span class=\"token punctuation\">,</span> expect <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'@playwright/test'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> login <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'../helpers/auth'</span><span class=\"token punctuation\">;</span>\n\ntest<span class=\"token punctuation\">.</span><span class=\"token function\">describe</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Task management'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    test<span class=\"token punctuation\">.</span><span class=\"token function\">beforeEach</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> page <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">await</span> <span class=\"token function\">login</span><span class=\"token punctuation\">(</span>page<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token function\">test</span><span class=\"token punctuation\">(</span><span class=\"token string\">'user can create a new task'</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> page <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">await</span> page<span class=\"token punctuation\">.</span><span class=\"token function\">goto</span><span class=\"token punctuation\">(</span><span class=\"token string\">'/tasks'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">await</span> page<span class=\"token punctuation\">.</span><span class=\"token function\">click</span><span class=\"token punctuation\">(</span><span class=\"token string\">'[data-testid=\"create-task-button\"]'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token keyword\">await</span> page<span class=\"token punctuation\">.</span><span class=\"token function\">fill</span><span class=\"token punctuation\">(</span><span class=\"token string\">'[data-testid=\"title-input\"]'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Write blog post'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">await</span> page<span class=\"token punctuation\">.</span><span class=\"token function\">fill</span><span class=\"token punctuation\">(</span><span class=\"token string\">'[data-testid=\"description-input\"]'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'About test automation'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">await</span> page<span class=\"token punctuation\">.</span><span class=\"token function\">click</span><span class=\"token punctuation\">(</span><span class=\"token string\">'[data-testid=\"submit-button\"]'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token keyword\">await</span> <span class=\"token function\">expect</span><span class=\"token punctuation\">(</span>page<span class=\"token punctuation\">.</span><span class=\"token function\">locator</span><span class=\"token punctuation\">(</span><span class=\"token string\">'[data-testid=\"task-card\"]'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">first</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token function\">toContainText</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Write blog post'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token function\">test</span><span class=\"token punctuation\">(</span><span class=\"token string\">'user can delete a task'</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> page <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">await</span> page<span class=\"token punctuation\">.</span><span class=\"token function\">goto</span><span class=\"token punctuation\">(</span><span class=\"token string\">'/tasks'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">const</span> initialCount <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> page<span class=\"token punctuation\">.</span><span class=\"token function\">locator</span><span class=\"token punctuation\">(</span><span class=\"token string\">'[data-testid=\"task-card\"]'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">count</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        page<span class=\"token punctuation\">.</span><span class=\"token function\">on</span><span class=\"token punctuation\">(</span><span class=\"token string\">'dialog'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span>dialog<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> dialog<span class=\"token punctuation\">.</span><span class=\"token function\">accept</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">await</span> page<span class=\"token punctuation\">.</span><span class=\"token function\">locator</span><span class=\"token punctuation\">(</span><span class=\"token string\">'[data-testid=\"task-card\"]'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">first</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token function\">locator</span><span class=\"token punctuation\">(</span><span class=\"token string\">'[data-testid=\"delete-button\"]'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">click</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token keyword\">await</span> <span class=\"token function\">expect</span><span class=\"token punctuation\">(</span>page<span class=\"token punctuation\">.</span><span class=\"token function\">locator</span><span class=\"token punctuation\">(</span><span class=\"token string\">'[data-testid=\"task-card\"]'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token function\">toHaveCount</span><span class=\"token punctuation\">(</span>initialCount <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 900px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 84.5%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAIAAABSJhvpAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB3ElEQVQ4y42T63LaMBCFeY/g+C7LWL7IF2wImEKTMpM27/86/SRBi5M/mTlo1rs62rMXVmGoU7EVYpskfRi1UdTieX6uvoNVFHf55gVIuYfm4PuNCfu179cYQdCEYevsBRmv1q9t+zbu/uj2FXT9NcsmQ7BCMMrqx27/gXO9VgtyEGpioG4uRTlb45ykg81mVGCgqx+uaTp4n8h3Vdrl8bwSfJKH5+mp+OpfUcmmOBTFEahyjpMer2+r/Y+gScVoGrnkrwhU9dloVnNVcx6DQNuet6E7Q021bfeWplvPW8rmHsVwxnEXRx3Nd8ofoDjXVvYjnOxGZBOZN5sDacv6jEGH0Jnn+9zYBwwhp01xlPleyh1AlCHzBqqm/QfiG/1znH5vx/eu/9UNV07TfH0Zx3cMlOMhDQaUW8PYLWKZ3JHQjK2cnQpaiJN7VXXKsp3MX1R54o5SR3Td5kxAVSc2jCWV9jaFEBZiTJKBmYvM2X2Sbk0JYoTCgE3DVHlEpFIznNxWRUJxfyJOBvciZPz0HD+fTAFyh4y6vvAeGR7J2Y1sOAAyTCBtcuo1mSmPbpnAXbaB0y8mRugIPOTIqU1jZDMqemAfHr5MmGHalVp8GtzmzM9uv2bVvvk3/oe/LkWm8/x87CYAAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <picture>\n          <source\n              srcset=\"/static/e7a84cb0413b86977be6a93766980055/28a80/test-playwright.webp 400w,\n/static/e7a84cb0413b86977be6a93766980055/8d2ea/test-playwright.webp 800w,\n/static/e7a84cb0413b86977be6a93766980055/90807/test-playwright.webp 900w\"\n              sizes=\"(max-width: 900px) 100vw, 900px\"\n              type=\"image/webp\"\n            />\n          <source\n            srcset=\"/static/e7a84cb0413b86977be6a93766980055/a3397/test-playwright.png 400w,\n/static/e7a84cb0413b86977be6a93766980055/a331c/test-playwright.png 800w,\n/static/e7a84cb0413b86977be6a93766980055/385f2/test-playwright.png 900w\"\n            sizes=\"(max-width: 900px) 100vw, 900px\"\n            type=\"image/png\"\n          />\n          <img\n            class=\"gatsby-resp-image-image\"\n            src=\"/static/e7a84cb0413b86977be6a93766980055/385f2/test-playwright.png\"\n            alt=\"Playwright E2Eテスト実行結果\"\n            title=\"Playwright E2Eテスト実行結果\"\n            loading=\"lazy\"\n            style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n          />\n        </picture>\n    </span></p>\n<p>Claude Code は React コンポーネントのソースを読み取り、<code class=\"language-text\">data-testid</code> を使ったセレクタを正確に生成しています。</p>\n<p>ダイアログ形式のUIにも対応した操作手順まで含まれていました。</p>\n<p>E2E テストを導入すると、テストの自動化だけでなく、実行画面を「証跡」として残せるという副次的なメリットもあります。</p>\n<h3>5.1 スクリーンショットをエビデンスとして残せる</h3>\n<p>Playwright の利点のひとつが、<strong>実行時のスクリーンショットを自動で保存し、テストのエビデンスとして残せる</strong>ことです。撮影タイミングは設定で切り替えられます。</p>\n<ul>\n<li>screenshot: 'only-on-failure' … テストが失敗したときだけ自動撮影する</li>\n<li>screenshot: 'on' … 毎回自動撮影する</li>\n<li>await page.screenshot({ path: 'evidence/create-task.png' }) … 任意のタイミングで撮影する</li>\n</ul>\n<p>撮影した画像は 'test-results/' 配下に出力され、HTMLレポート（'playwright show-report'）からまとめて確認できます。<br>\n手動テストで行っていた証跡スクリーンショットの取得をコードに組み込めるため、不具合報告やレビュー時の説明材料としても活用できます。</p>\n<p>下は、テスト実行中に Playwright が撮影した画面です。<br>\nテストがフォームに自動入力した内容（'【E2Eテスト】…' というテストデータ）がそのまま写っており、どの操作時点のスクリーンショットなのかが一目で分かります。</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1280px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 70.25%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAIAAACgpqunAAAACXBIWXMAAAsTAAALEwEAmpwYAAABo0lEQVQoz4WT3U7CQBCFeRWNoKjsbndbSwKUC15KryX+JoDegPhO+gZqAgrht7RLCwjo2RaMCuhkspm0/XrOTKcRzgVfBArxd+i6oBTJDYOjjoSctgzUxoZYKAh+eMi3t7SDfU3BhBBKKUihTGixWCwaje78DFzBLTyg65wxHt/TCFnAYFmgys1k8jSfv7y6LhQKxWIJB7JUujk7PzdNk4eN6Uhl4UuZoaCMZrPZVqfrjyfz+UcY86CSUlqWxRjT0atYjEnBIBMJwphGCclkrPpr03a8dsdGyuHYcX3Pf2+1u2tguMUlaKNnWEilMk8vjXbXbbWdbm9oD3xXTjx/9tbo472rsJYgkIVtDa2n05lavee4477t2YMRCtcdD71Zrd5Ppzcow7kIYHh7fqn3bQnDrhwhHTmaTKf2oLdWOYQpV7aVMmbzNafwaDY+Hh9kLmdB4/fAMKpg3uqDmWby5va2Wr2vVO6Q5fIdjouL8slxMZVKBrsgfvacUGjwmQWMrG5IbHcnHo/yb9u7FlYPHK0Lwzj6vuS/YBpsdrhm//0fS/gTYQ4hJ9lJjR4AAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <picture>\n          <source\n              srcset=\"/static/f12ba3ce72d4dc2a2205be57205e09fa/28a80/test-screenshot.webp 400w,\n/static/f12ba3ce72d4dc2a2205be57205e09fa/8d2ea/test-screenshot.webp 800w,\n/static/f12ba3ce72d4dc2a2205be57205e09fa/4b075/test-screenshot.webp 1280w\"\n              sizes=\"(max-width: 1280px) 100vw, 1280px\"\n              type=\"image/webp\"\n            />\n          <source\n            srcset=\"/static/f12ba3ce72d4dc2a2205be57205e09fa/a3397/test-screenshot.png 400w,\n/static/f12ba3ce72d4dc2a2205be57205e09fa/a331c/test-screenshot.png 800w,\n/static/f12ba3ce72d4dc2a2205be57205e09fa/26c69/test-screenshot.png 1280w\"\n            sizes=\"(max-width: 1280px) 100vw, 1280px\"\n            type=\"image/png\"\n          />\n          <img\n            class=\"gatsby-resp-image-image\"\n            src=\"/static/f12ba3ce72d4dc2a2205be57205e09fa/26c69/test-screenshot.png\"\n            alt=\"テスト実行中にPlaywrightが撮影した画面（エビデンス）。テストが自動入力したデータが写っている\"\n            title=\"テスト実行中にPlaywrightが撮影した画面（エビデンス）。テストが自動入力したデータが写っている\"\n            loading=\"lazy\"\n            style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n          />\n        </picture>\n    </span></p>\n<hr>\n<h2>■ まとめ</h2>\n<p>Claude Code は、ソースコードを読み取ったうえで、Unit・Feature・E2E の3層すべてを実装に即して生成し、実行・修正まで一貫して任せられます。</p>\n<p>テストの作成コストを大きく下げられるため、これまで手が回らなかったテスト整備にもぜひ活用してみてください。</p>","frontmatter":{"title":"Claude Codeでテストを自動生成する","date":"2026/07/02","author":"森正"}}},"pageContext":{"pathSlug":"/claude-code-test-automation","prev":{"frontmatter":{"path":"/certbot-ip-address","title":"Let's EncryptでIPアドレス証明書を発行してみた"}},"next":null}},"staticQueryHashes":["1904330086","3409585628","4208282778"]}