<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Ui-Test on Tarragon</title><link>https://tarrragon.github.io/blog/tags/ui-test/</link><description>Recent content in Ui-Test on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Fri, 19 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/ui-test/index.xml" rel="self" type="application/rss+xml"/><item><title>Screen State Test</title><link>https://tarrragon.github.io/blog/testing/knowledge-cards/screen-state-test/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/testing/knowledge-cards/screen-state-test/</guid><description>&lt;p>Screen state test 的核心概念是「驗證畫面層級的狀態機是否完整 — 每個狀態下使用者看到什麼、能操作什麼、怎麼離開」。它的斷言對象是使用者可見的畫面，和 unit test（斷言函式回傳值）及 &lt;a href="https://tarrragon.github.io/blog/testing/knowledge-cards/protocol-integration-test/" data-link-title="Protocol Integration Test" data-link-desc="驗證程式碼和真實外部服務之間的協議互動是否正確的 test 層級">protocol integration test&lt;/a>（斷言協議互動）職責不同。可先對照&lt;a href="https://tarrragon.github.io/blog/ux-design/knowledge-cards/screen-state-matrix/" data-link-title="畫面狀態矩陣" data-link-desc="說明用四欄表格（顯示/可用操作/進入條件/退出路徑）系統性地暴露畫面導航缺口的設計工具">畫面狀態矩陣&lt;/a>。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Screen state test 是測試三層中的第三層。Unit test 驗證程式碼邏輯，protocol integration test 驗證協議互動，screen state test 驗證畫面狀態。同一段程式碼可能 unit test 通過但 screen state test 失敗 — 因為 UI binding 的問題讓正確的邏輯沒有反映到畫面上。&lt;/p>
&lt;h2 id="可觀察訊號與例子">可觀察訊號與例子&lt;/h2>
&lt;p>需要 screen state test 的訊號是畫面有多個狀態（loading / connected / error / disconnected）且狀態轉換邏輯複雜。&lt;a href="https://tarrragon.github.io/blog/ux-design/knowledge-cards/screen-state-matrix/" data-link-title="畫面狀態矩陣" data-link-desc="說明用四欄表格（顯示/可用操作/進入條件/退出路徑）系統性地暴露畫面導航缺口的設計工具">畫面狀態矩陣&lt;/a>直接轉成 test case — 矩陣中每個狀態的「顯示」「可用操作」「退出路徑」各對應一個 assertion。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>Screen state test 要決定用什麼工具驗證畫面（widget test / integration test / Playwright）、斷言的粒度（元素存在 / 文字內容 / 視覺比對）、和狀態的觸發方式（mock 觸發狀態切換 / 真實操作觸發）。&lt;/p></description><content:encoded><![CDATA[<p>Screen state test 的核心概念是「驗證畫面層級的狀態機是否完整 — 每個狀態下使用者看到什麼、能操作什麼、怎麼離開」。它的斷言對象是使用者可見的畫面，和 unit test（斷言函式回傳值）及 <a href="/blog/testing/knowledge-cards/protocol-integration-test/" data-link-title="Protocol Integration Test" data-link-desc="驗證程式碼和真實外部服務之間的協議互動是否正確的 test 層級">protocol integration test</a>（斷言協議互動）職責不同。可先對照<a href="/blog/ux-design/knowledge-cards/screen-state-matrix/" data-link-title="畫面狀態矩陣" data-link-desc="說明用四欄表格（顯示/可用操作/進入條件/退出路徑）系統性地暴露畫面導航缺口的設計工具">畫面狀態矩陣</a>。</p>
<h2 id="概念位置">概念位置</h2>
<p>Screen state test 是測試三層中的第三層。Unit test 驗證程式碼邏輯，protocol integration test 驗證協議互動，screen state test 驗證畫面狀態。同一段程式碼可能 unit test 通過但 screen state test 失敗 — 因為 UI binding 的問題讓正確的邏輯沒有反映到畫面上。</p>
<h2 id="可觀察訊號與例子">可觀察訊號與例子</h2>
<p>需要 screen state test 的訊號是畫面有多個狀態（loading / connected / error / disconnected）且狀態轉換邏輯複雜。<a href="/blog/ux-design/knowledge-cards/screen-state-matrix/" data-link-title="畫面狀態矩陣" data-link-desc="說明用四欄表格（顯示/可用操作/進入條件/退出路徑）系統性地暴露畫面導航缺口的設計工具">畫面狀態矩陣</a>直接轉成 test case — 矩陣中每個狀態的「顯示」「可用操作」「退出路徑」各對應一個 assertion。</p>
<h2 id="設計責任">設計責任</h2>
<p>Screen state test 要決定用什麼工具驗證畫面（widget test / integration test / Playwright）、斷言的粒度（元素存在 / 文字內容 / 視覺比對）、和狀態的觸發方式（mock 觸發狀態切換 / 真實操作觸發）。</p>
]]></content:encoded></item><item><title>模組四：自動化 UI 驗證</title><link>https://tarrragon.github.io/blog/testing/04-ui-automation/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/testing/04-ui-automation/</guid><description>&lt;p>回答「畫面上的東西是否如設計工作」。狀態矩陣直接轉成 test case。&lt;/p>
&lt;h2 id="待寫章節">待寫章節&lt;/h2>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Widget test 的狀態覆蓋策略（從狀態矩陣推導 test case）&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> 導航路徑 test（back 按鈕、route 可達性）&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Playwright 瀏覽器驗證流程&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> 螢幕截圖比對（visual regression）&lt;/li>
&lt;/ul>
&lt;h2 id="跨分類引用">跨分類引用&lt;/h2>
&lt;ul>
&lt;li>← &lt;a href="https://tarrragon.github.io/blog/ux-design/01-screen-state-machine/" data-link-title="模組一：畫面狀態機設計" data-link-desc="畫面狀態矩陣（顯示 / 操作 / 進入 / 退出）— 退出路徑為空 = UX 死胡同">ux-design 模組一 畫面狀態機&lt;/a>：狀態矩陣是 test case 的 SOT&lt;/li>
&lt;li>← &lt;a href="https://tarrragon.github.io/blog/ux-design/05-navigation-patterns/" data-link-title="模組五：導航模式" data-link-desc="Push/pop stack、GoRouter 命名路由、tab bar、drawer — 導航方法選擇是設計決策">ux-design 模組五 導航模式&lt;/a>：go vs push 語意影響 test 斷言&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>回答「畫面上的東西是否如設計工作」。狀態矩陣直接轉成 test case。</p>
<h2 id="待寫章節">待寫章節</h2>
<ul>
<li><input checked="" disabled="" type="checkbox"> Widget test 的狀態覆蓋策略（從狀態矩陣推導 test case）</li>
<li><input checked="" disabled="" type="checkbox"> 導航路徑 test（back 按鈕、route 可達性）</li>
<li><input checked="" disabled="" type="checkbox"> Playwright 瀏覽器驗證流程</li>
<li><input checked="" disabled="" type="checkbox"> 螢幕截圖比對（visual regression）</li>
</ul>
<h2 id="跨分類引用">跨分類引用</h2>
<ul>
<li>← <a href="/blog/ux-design/01-screen-state-machine/" data-link-title="模組一：畫面狀態機設計" data-link-desc="畫面狀態矩陣（顯示 / 操作 / 進入 / 退出）— 退出路徑為空 = UX 死胡同">ux-design 模組一 畫面狀態機</a>：狀態矩陣是 test case 的 SOT</li>
<li>← <a href="/blog/ux-design/05-navigation-patterns/" data-link-title="模組五：導航模式" data-link-desc="Push/pop stack、GoRouter 命名路由、tab bar、drawer — 導航方法選擇是設計決策">ux-design 模組五 導航模式</a>：go vs push 語意影響 test 斷言</li>
</ul>
]]></content:encoded></item><item><title>螢幕截圖比對</title><link>https://tarrragon.github.io/blog/testing/04-ui-automation/visual-regression/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/testing/04-ui-automation/visual-regression/</guid><description>&lt;p>螢幕截圖比對（visual regression testing）用基準截圖（baseline）和當前截圖的像素差異來偵測非預期的視覺變化。這一層驗證的是「畫面看起來是否和上次一樣」，捕捉 CSS 變更、layout 偏移、字體替換等邏輯 test 無法發現的視覺問題。&lt;/p>
&lt;h2 id="運作方式">運作方式&lt;/h2>
&lt;h3 id="建立-baseline">建立 baseline&lt;/h3>
&lt;p>第一次執行時擷取每個測試畫面的螢幕截圖，儲存為 baseline。Baseline 代表「目前正確的視覺狀態」。&lt;/p>
&lt;h3 id="比對差異">比對差異&lt;/h3>
&lt;p>後續執行時擷取當前截圖，和 baseline 逐像素比對。差異超過閾值時 test 失敗，產出 diff 圖片標示差異區域。&lt;/p>
&lt;h3 id="更新-baseline">更新 baseline&lt;/h3>
&lt;p>視覺變更是刻意的（新設計、改佈局）時，用新截圖覆蓋 baseline。更新 baseline 是明確的決策 — 代表「新的視覺狀態是正確的」。&lt;/p>
&lt;h2 id="playwright-的截圖比對">Playwright 的截圖比對&lt;/h2>
&lt;p>Playwright 內建 &lt;code>toHaveScreenshot()&lt;/code> 方法：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="nx">test&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;terminal screen matches baseline&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="p">({&lt;/span> &lt;span class="nx">page&lt;/span> &lt;span class="p">})&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl"> &lt;span class="k">await&lt;/span> &lt;span class="nx">page&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="kr">goto&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;http://localhost:8080/terminal&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl"> &lt;span class="k">await&lt;/span> &lt;span class="nx">page&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">waitForSelector&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;[data-testid=&amp;#34;terminal-screen&amp;#34;]&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl"> &lt;span class="k">await&lt;/span> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">page&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">toHaveScreenshot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;terminal-connected.png&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl"> &lt;span class="nx">maxDiffPixelRatio&lt;/span>: &lt;span class="kt">0.01&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// 允許 1% 像素差異
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="p">});&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>首次執行時自動建立 baseline 截圖，後續執行時自動比對。Diff 圖片儲存在 test results 目錄。&lt;/p>
&lt;h2 id="diff-閾值設定">Diff 閾值設定&lt;/h2>
&lt;p>像素比對的閾值影響 test 的敏感度：&lt;/p>
&lt;p>&lt;strong>過低（0.001）&lt;/strong>：anti-aliasing 差異、字體渲染微調、次像素定位變化都會觸發失敗。Test 頻繁因無關變化而失敗（flaky）。&lt;/p>
&lt;p>&lt;strong>過高（0.1）&lt;/strong>：小型 layout 偏移、顏色微調、邊框消失可能不被偵測。Test 的保護力下降。&lt;/p>
&lt;p>&lt;strong>建議起點（0.01）&lt;/strong>：允許 1% 的像素差異。能容忍 anti-aliasing 差異，同時捕捉有意義的視覺變化。根據實際 flaky 頻率調整。&lt;/p>
&lt;h2 id="baseline-管理">Baseline 管理&lt;/h2>
&lt;h3 id="版本控制">版本控制&lt;/h3>
&lt;p>Baseline 截圖加入 git。每次視覺變更的 PR 包含 baseline 更新，reviewer 從 diff 中看到「視覺變化了什麼」。&lt;/p>
&lt;p>Baseline 檔案較大（PNG，數十 KB 到數百 KB）。Git LFS 適合管理這類二進位檔案。&lt;/p>
&lt;h3 id="跨平台差異">跨平台差異&lt;/h3>
&lt;p>不同作業系統的字體渲染、anti-aliasing 演算法不同。同一段 HTML 在 macOS 和 Linux 上的截圖會有微小差異。&lt;/p>
&lt;p>處理策略：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>一個平台一套 baseline&lt;/strong>：macOS 和 Linux 各自維護 baseline。CI 環境固定在一個平台。&lt;/li>
&lt;li>&lt;strong>只在 CI 比對&lt;/strong>：本地開發不跑截圖比對（平台差異導致 flaky），CI 環境固定平台後比對。&lt;/li>
&lt;/ul>
&lt;h3 id="動態內容">動態內容&lt;/h3>
&lt;p>畫面中有動態內容（時間戳、隨機 ID、動畫）時，截圖每次都不同。&lt;/p>
&lt;p>處理策略：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>遮蔽動態區域&lt;/strong>：截圖前用 CSS 隱藏動態元素，或在截圖比對時指定忽略區域。&lt;/li>
&lt;li>&lt;strong>固定動態值&lt;/strong>：test 中 mock 時間和隨機數，讓畫面內容確定。&lt;/li>
&lt;li>&lt;strong>只截靜態區域&lt;/strong>：用 element screenshot（&lt;code>locator.screenshot()&lt;/code>）而非 full page screenshot，只截不含動態內容的區域。&lt;/li>
&lt;/ul>
&lt;h2 id="和其他-test-層的關係">和其他 test 層的關係&lt;/h2>
&lt;p>截圖比對是 UI test 的最外層 — 驗證視覺呈現而非邏輯行為。它和 widget test（驗證 widget 結構）、導航 test（驗證路由行為）互補：&lt;/p>
&lt;p>widget test 通過但截圖比對失敗 = 邏輯正確但視覺不對（CSS bug）。截圖比對通過但 widget test 失敗 = 視覺沒變但邏輯壞了（功能 bug 還沒影響到視覺）。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>狀態覆蓋策略 → &lt;a href="https://tarrragon.github.io/blog/testing/04-ui-automation/state-coverage-strategy/" data-link-title="Widget test 的狀態覆蓋策略" data-link-desc="從畫面狀態矩陣推導 widget test case — 每個狀態的顯示、操作、退出路徑都是獨立的斷言目標">Widget test 的狀態覆蓋策略&lt;/a>&lt;/li>
&lt;li>Playwright 驗證流程 → &lt;a href="https://tarrragon.github.io/blog/testing/04-ui-automation/playwright-verification/" data-link-title="Playwright 瀏覽器驗證流程" data-link-desc="用 Playwright 驗證 web 版本的 UI 行為 — test 結構、selector 策略、和 widget test 的互補關係">Playwright 瀏覽器驗證流程&lt;/a>&lt;/li>
&lt;li>畫面狀態矩陣 → &lt;a href="https://tarrragon.github.io/blog/ux-design/01-screen-state-machine/state-matrix-definition/" data-link-title="畫面狀態矩陣的定義與填寫方法" data-link-desc="四欄矩陣（顯示 / 可用操作 / 進入條件 / 退出路徑）的定義、填寫步驟和檢查規則 — 退出路徑為空 = UX 死胡同">ux-design 模組一 畫面狀態矩陣&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>螢幕截圖比對（visual regression testing）用基準截圖（baseline）和當前截圖的像素差異來偵測非預期的視覺變化。這一層驗證的是「畫面看起來是否和上次一樣」，捕捉 CSS 變更、layout 偏移、字體替換等邏輯 test 無法發現的視覺問題。</p>
<h2 id="運作方式">運作方式</h2>
<h3 id="建立-baseline">建立 baseline</h3>
<p>第一次執行時擷取每個測試畫面的螢幕截圖，儲存為 baseline。Baseline 代表「目前正確的視覺狀態」。</p>
<h3 id="比對差異">比對差異</h3>
<p>後續執行時擷取當前截圖，和 baseline 逐像素比對。差異超過閾值時 test 失敗，產出 diff 圖片標示差異區域。</p>
<h3 id="更新-baseline">更新 baseline</h3>
<p>視覺變更是刻意的（新設計、改佈局）時，用新截圖覆蓋 baseline。更新 baseline 是明確的決策 — 代表「新的視覺狀態是正確的」。</p>
<h2 id="playwright-的截圖比對">Playwright 的截圖比對</h2>
<p>Playwright 內建 <code>toHaveScreenshot()</code> 方法：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="ln">1</span><span class="cl"><span class="nx">test</span><span class="p">(</span><span class="s1">&#39;terminal screen matches baseline&#39;</span><span class="p">,</span> <span class="kr">async</span> <span class="p">({</span> <span class="nx">page</span> <span class="p">})</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">  <span class="k">await</span> <span class="nx">page</span><span class="p">.</span><span class="kr">goto</span><span class="p">(</span><span class="s1">&#39;http://localhost:8080/terminal&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">  <span class="k">await</span> <span class="nx">page</span><span class="p">.</span><span class="nx">waitForSelector</span><span class="p">(</span><span class="s1">&#39;[data-testid=&#34;terminal-screen&#34;]&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">  
</span></span><span class="line"><span class="ln">5</span><span class="cl">  <span class="k">await</span> <span class="nx">expect</span><span class="p">(</span><span class="nx">page</span><span class="p">).</span><span class="nx">toHaveScreenshot</span><span class="p">(</span><span class="s1">&#39;terminal-connected.png&#39;</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">    <span class="nx">maxDiffPixelRatio</span>: <span class="kt">0.01</span><span class="p">,</span>  <span class="c1">// 允許 1% 像素差異
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"></span>  <span class="p">});</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="p">});</span></span></span></code></pre></div><p>首次執行時自動建立 baseline 截圖，後續執行時自動比對。Diff 圖片儲存在 test results 目錄。</p>
<h2 id="diff-閾值設定">Diff 閾值設定</h2>
<p>像素比對的閾值影響 test 的敏感度：</p>
<p><strong>過低（0.001）</strong>：anti-aliasing 差異、字體渲染微調、次像素定位變化都會觸發失敗。Test 頻繁因無關變化而失敗（flaky）。</p>
<p><strong>過高（0.1）</strong>：小型 layout 偏移、顏色微調、邊框消失可能不被偵測。Test 的保護力下降。</p>
<p><strong>建議起點（0.01）</strong>：允許 1% 的像素差異。能容忍 anti-aliasing 差異，同時捕捉有意義的視覺變化。根據實際 flaky 頻率調整。</p>
<h2 id="baseline-管理">Baseline 管理</h2>
<h3 id="版本控制">版本控制</h3>
<p>Baseline 截圖加入 git。每次視覺變更的 PR 包含 baseline 更新，reviewer 從 diff 中看到「視覺變化了什麼」。</p>
<p>Baseline 檔案較大（PNG，數十 KB 到數百 KB）。Git LFS 適合管理這類二進位檔案。</p>
<h3 id="跨平台差異">跨平台差異</h3>
<p>不同作業系統的字體渲染、anti-aliasing 演算法不同。同一段 HTML 在 macOS 和 Linux 上的截圖會有微小差異。</p>
<p>處理策略：</p>
<ul>
<li><strong>一個平台一套 baseline</strong>：macOS 和 Linux 各自維護 baseline。CI 環境固定在一個平台。</li>
<li><strong>只在 CI 比對</strong>：本地開發不跑截圖比對（平台差異導致 flaky），CI 環境固定平台後比對。</li>
</ul>
<h3 id="動態內容">動態內容</h3>
<p>畫面中有動態內容（時間戳、隨機 ID、動畫）時，截圖每次都不同。</p>
<p>處理策略：</p>
<ul>
<li><strong>遮蔽動態區域</strong>：截圖前用 CSS 隱藏動態元素，或在截圖比對時指定忽略區域。</li>
<li><strong>固定動態值</strong>：test 中 mock 時間和隨機數，讓畫面內容確定。</li>
<li><strong>只截靜態區域</strong>：用 element screenshot（<code>locator.screenshot()</code>）而非 full page screenshot，只截不含動態內容的區域。</li>
</ul>
<h2 id="和其他-test-層的關係">和其他 test 層的關係</h2>
<p>截圖比對是 UI test 的最外層 — 驗證視覺呈現而非邏輯行為。它和 widget test（驗證 widget 結構）、導航 test（驗證路由行為）互補：</p>
<p>widget test 通過但截圖比對失敗 = 邏輯正確但視覺不對（CSS bug）。截圖比對通過但 widget test 失敗 = 視覺沒變但邏輯壞了（功能 bug 還沒影響到視覺）。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>狀態覆蓋策略 → <a href="/blog/testing/04-ui-automation/state-coverage-strategy/" data-link-title="Widget test 的狀態覆蓋策略" data-link-desc="從畫面狀態矩陣推導 widget test case — 每個狀態的顯示、操作、退出路徑都是獨立的斷言目標">Widget test 的狀態覆蓋策略</a></li>
<li>Playwright 驗證流程 → <a href="/blog/testing/04-ui-automation/playwright-verification/" data-link-title="Playwright 瀏覽器驗證流程" data-link-desc="用 Playwright 驗證 web 版本的 UI 行為 — test 結構、selector 策略、和 widget test 的互補關係">Playwright 瀏覽器驗證流程</a></li>
<li>畫面狀態矩陣 → <a href="/blog/ux-design/01-screen-state-machine/state-matrix-definition/" data-link-title="畫面狀態矩陣的定義與填寫方法" data-link-desc="四欄矩陣（顯示 / 可用操作 / 進入條件 / 退出路徑）的定義、填寫步驟和檢查規則 — 退出路徑為空 = UX 死胡同">ux-design 模組一 畫面狀態矩陣</a></li>
</ul>
]]></content:encoded></item></channel></rss>