<?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>External-Device on Tarragon</title><link>https://tarrragon.github.io/blog/tags/external-device/</link><description>Recent content in External-Device on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Fri, 17 Jul 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/external-device/index.xml" rel="self" type="application/rss+xml"/><item><title>T.C9 外接螢幕漏通知 — 訊息序列斷言與訂閱盲區</title><link>https://tarrragon.github.io/blog/testing/cases/outbox-sequence-external-display/</link><pubDate>Fri, 17 Jul 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/testing/cases/outbox-sequence-external-display/</guid><description>&lt;p>一條新流程上線後，外接第二螢幕停在上一個畫面——交易的另一方看不到當前狀態，主畫面卻一切正常、沒有任何錯誤浮現。漏通知的根因在機制，換誰開發都會再發生；驗證邊界則劃在出口——斷言送出的資料流與時序，裝置端的渲染交由裝置自己的系統驗證。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>主裝置之外還有一面外接第二螢幕——面向交易另一方（例如顧客）的畫面，主畫面的狀態要同步過去。同步機制有兩種形態：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>形態&lt;/th>
 &lt;th>例子&lt;/th>
 &lt;th>特性&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>訂閱驅動&lt;/td>
 &lt;td>本地清單變動 → 自動推送&lt;/td>
 &lt;td>資料流動就通知，不會漏&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>顯式呼叫&lt;/td>
 &lt;td>某流程完成後手動推送一次&lt;/td>
 &lt;td>每開一條新流程都要記得補一筆，容易漏&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>漏通知 bug 的成因：訂閱掛在「本地清單」的變動上，但有一條新流程走的是「遠端資料直接進入結帳」——資料不經過本地清單，訂閱永遠不觸發；而顯式呼叫沒人記得補。這是訂閱盲區：訂閱的資料源沒涵蓋所有會改變顯示內容的路徑。第二螢幕停在上一個畫面。&lt;/p>
&lt;p>修復後補的驗證：所有送往第二螢幕的訊息都經過單一出口，測試注入一個&lt;strong>錄音假件&lt;/strong>攔下每筆訊息（動作類型＋內容），斷言序列——進結帳要推「畫面切換＋品項清單」與「付款資訊」；完成時推「完成」；離開才推「清除」；且**「完成」必須先於「清除」**（順序顛倒時另一方看不到交易結果畫面，這是明文的時序約束）。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>顯式呼叫是結構性風險點，可以被系統性審查&lt;/strong>。這份審查是一張可窮舉的清單：列出所有「會改變第二螢幕應顯示內容」的流程，逐一檢查它的資料走訂閱路徑、還是需要顯式推送。清單窮舉取代開發時的臨場記憶。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>驗證邊界劃在「送出了什麼」&lt;/strong>。第二螢幕是另一個執行環境（甚至另一個引擎），它怎麼渲染不是這個測試套件能觸及的。正確的可測物是出口處的訊息流：內容、順序、時機。裝置端的渲染屬於另一個系統的職責。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>序列斷言強於存在斷言&lt;/strong>。只斷言「有送完成訊息」抓不到「完成在清除之後」這種順序錯誤——而順序錯誤的使用者體驗傷害（看不到結果畫面）不亞於漏送。對有時序約束的訊息流，斷言用索引比較（完成的位置 &amp;lt; 清除的位置），而非個別存在性。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="策略">策略&lt;/h2>
&lt;ol>
&lt;li>&lt;strong>收斂出口&lt;/strong>：所有外送訊息經過單一方法，測試才有唯一的攔截點。出口分散的程式先重構再談驗證。&lt;/li>
&lt;li>&lt;strong>錄音假件&lt;/strong>：以假件替換傳輸層，記錄（動作、內容）序列供斷言；不 mock 到業務層——業務到出口之間的編排正是要驗的對象。&lt;/li>
&lt;li>&lt;strong>時序約束寫成斷言&lt;/strong>：程式註解裡「A 必須在 B 之前」的每一句話，都應該有一條測試用索引比較把它鎖住。&lt;/li>
&lt;li>&lt;strong>長期方向：把顯式呼叫收編為訂閱&lt;/strong>。每多一條訂閱驅動的路徑，就少一個「記得補推送」的人為環節。訂閱化是消除這類 bug 的結構解。&lt;/li>
&lt;/ol>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>這類流程測試的地基 → &lt;a href="https://tarrragon.github.io/blog/testing/01-test-strategy-layers/semantic-fake-backend/" data-link-title="語意級假後端與流程測試" data-link-desc="bug 的成因是對後端行為的假設錯誤、由測試餵資料的 stub 驗證不出來時：建一個持有狀態、模擬已證實後端行為的假後端（test double 分類的 fake），讓流程測試走完整的多服務互動鏈">語意級假後端與流程測試&lt;/a>&lt;/li>
&lt;li>序列斷言的設計品質 → &lt;a href="https://tarrragon.github.io/blog/testing/05-test-design-judgment/assertion-quality/" data-link-title="Assertion 品質三問" data-link-desc="斷言的是行為嗎？能區分正確和錯誤嗎？會 flaky 嗎？— 三個問題判斷 assertion 是否有效">斷言品質三問&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>一條新流程上線後，外接第二螢幕停在上一個畫面——交易的另一方看不到當前狀態，主畫面卻一切正常、沒有任何錯誤浮現。漏通知的根因在機制，換誰開發都會再發生；驗證邊界則劃在出口——斷言送出的資料流與時序，裝置端的渲染交由裝置自己的系統驗證。</p>
<h2 id="觀察">觀察</h2>
<p>主裝置之外還有一面外接第二螢幕——面向交易另一方（例如顧客）的畫面，主畫面的狀態要同步過去。同步機制有兩種形態：</p>
<table>
  <thead>
      <tr>
          <th>形態</th>
          <th>例子</th>
          <th>特性</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>訂閱驅動</td>
          <td>本地清單變動 → 自動推送</td>
          <td>資料流動就通知，不會漏</td>
      </tr>
      <tr>
          <td>顯式呼叫</td>
          <td>某流程完成後手動推送一次</td>
          <td>每開一條新流程都要記得補一筆，容易漏</td>
      </tr>
  </tbody>
</table>
<p>漏通知 bug 的成因：訂閱掛在「本地清單」的變動上，但有一條新流程走的是「遠端資料直接進入結帳」——資料不經過本地清單，訂閱永遠不觸發；而顯式呼叫沒人記得補。這是訂閱盲區：訂閱的資料源沒涵蓋所有會改變顯示內容的路徑。第二螢幕停在上一個畫面。</p>
<p>修復後補的驗證：所有送往第二螢幕的訊息都經過單一出口，測試注入一個<strong>錄音假件</strong>攔下每筆訊息（動作類型＋內容），斷言序列——進結帳要推「畫面切換＋品項清單」與「付款資訊」；完成時推「完成」；離開才推「清除」；且**「完成」必須先於「清除」**（順序顛倒時另一方看不到交易結果畫面，這是明文的時序約束）。</p>
<h2 id="判讀">判讀</h2>
<ol>
<li>
<p><strong>顯式呼叫是結構性風險點，可以被系統性審查</strong>。這份審查是一張可窮舉的清單：列出所有「會改變第二螢幕應顯示內容」的流程，逐一檢查它的資料走訂閱路徑、還是需要顯式推送。清單窮舉取代開發時的臨場記憶。</p>
</li>
<li>
<p><strong>驗證邊界劃在「送出了什麼」</strong>。第二螢幕是另一個執行環境（甚至另一個引擎），它怎麼渲染不是這個測試套件能觸及的。正確的可測物是出口處的訊息流：內容、順序、時機。裝置端的渲染屬於另一個系統的職責。</p>
</li>
<li>
<p><strong>序列斷言強於存在斷言</strong>。只斷言「有送完成訊息」抓不到「完成在清除之後」這種順序錯誤——而順序錯誤的使用者體驗傷害（看不到結果畫面）不亞於漏送。對有時序約束的訊息流，斷言用索引比較（完成的位置 &lt; 清除的位置），而非個別存在性。</p>
</li>
</ol>
<h2 id="策略">策略</h2>
<ol>
<li><strong>收斂出口</strong>：所有外送訊息經過單一方法，測試才有唯一的攔截點。出口分散的程式先重構再談驗證。</li>
<li><strong>錄音假件</strong>：以假件替換傳輸層，記錄（動作、內容）序列供斷言；不 mock 到業務層——業務到出口之間的編排正是要驗的對象。</li>
<li><strong>時序約束寫成斷言</strong>：程式註解裡「A 必須在 B 之前」的每一句話，都應該有一條測試用索引比較把它鎖住。</li>
<li><strong>長期方向：把顯式呼叫收編為訂閱</strong>。每多一條訂閱驅動的路徑，就少一個「記得補推送」的人為環節。訂閱化是消除這類 bug 的結構解。</li>
</ol>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>這類流程測試的地基 → <a href="/blog/testing/01-test-strategy-layers/semantic-fake-backend/" data-link-title="語意級假後端與流程測試" data-link-desc="bug 的成因是對後端行為的假設錯誤、由測試餵資料的 stub 驗證不出來時：建一個持有狀態、模擬已證實後端行為的假後端（test double 分類的 fake），讓流程測試走完整的多服務互動鏈">語意級假後端與流程測試</a></li>
<li>序列斷言的設計品質 → <a href="/blog/testing/05-test-design-judgment/assertion-quality/" data-link-title="Assertion 品質三問" data-link-desc="斷言的是行為嗎？能區分正確和錯誤嗎？會 flaky 嗎？— 三個問題判斷 assertion 是否有效">斷言品質三問</a></li>
</ul>
]]></content:encoded></item></channel></rss>