<?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>Assertion on Tarragon</title><link>https://tarrragon.github.io/blog/tags/assertion/</link><description>Recent content in Assertion 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/assertion/index.xml" rel="self" type="application/rss+xml"/><item><title>Assertion 品質三問</title><link>https://tarrragon.github.io/blog/testing/05-test-design-judgment/assertion-quality/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/testing/05-test-design-judgment/assertion-quality/</guid><description>&lt;p>Assertion 是 test 的結論 — 「我認為程式碼的行為應該是 X」。Assertion 的品質決定了 test 的有效性：無效的 assertion 讓 test 通過但問題仍在，或讓 test 隨機失敗但問題不在程式碼。&lt;/p>
&lt;h2 id="三個判斷問題">三個判斷問題&lt;/h2>
&lt;h3 id="斷言的是行為嗎">斷言的是行為嗎&lt;/h3>
&lt;p>Assertion 應該斷言程式碼的外部可觀察行為（回傳值、狀態變化、副作用），而非內部實作細節（私有變數的值、呼叫次數、執行順序）。&lt;/p>
&lt;p>斷言行為的 test 在重構時不需要改 — 只要行為不變，test 就通過。斷言實作的 test 在任何內部調整時都會壞掉，即使行為完全正確。&lt;/p>
&lt;p>例：驗證「parser 正確解析紅色文字」時，斷言 token 的顏色屬性（行為）比斷言 parser 內部的 state machine 走了哪些步驟（實作）更穩定。&lt;/p>
&lt;h3 id="能區分正確和錯誤嗎">能區分正確和錯誤嗎&lt;/h3>
&lt;p>Assertion 應該在程式碼正確時通過、錯誤時失敗。如果 assertion 無論程式碼正確或錯誤都通過，這個 assertion 沒有提供保護。&lt;/p>
&lt;p>常見的無效 assertion：&lt;/p>
&lt;p>&lt;strong>斷言不為 null&lt;/strong>：&lt;code>expect(result, isNotNull)&lt;/code> 只驗證「有回傳值」，不驗證「回傳值正確」。回傳錯誤的值也會通過。&lt;/p>
&lt;p>&lt;strong>斷言型別&lt;/strong>：&lt;code>expect(result, isA&amp;lt;List&amp;gt;())&lt;/code> 只驗證「回傳 List」，不驗證 List 的內容。空 List 和錯誤內容的 List 都會通過。&lt;/p>
&lt;p>&lt;strong>斷言包含&lt;/strong>：&lt;code>expect(result, contains('error'))&lt;/code> 驗證字串包含 &amp;rsquo;error&amp;rsquo;，但如果回傳 &amp;rsquo;no error occurred&amp;rsquo;（正確情境）也包含 &amp;rsquo;error&amp;rsquo; — assertion 無法區分正確和錯誤。&lt;/p>
&lt;p>T.C3 的 parser test 斷言 &lt;code>expect(tokens.first, isA&amp;lt;TextToken&amp;gt;())&lt;/code> — 驗證 token 型別是 TextToken。但正確解析和透傳亂碼都可能產生 TextToken，assertion 無法區分（本章合成，TF-5 Derive — 透傳的靜默副作用和 assertion 的區分力有 tension）。&lt;/p>
&lt;h3 id="會-flaky-嗎">會 flaky 嗎&lt;/h3>
&lt;p>Assertion 是否依賴非確定性因素 — 時間、隨機數、外部服務狀態、執行順序。如果是，test 可能在程式碼正確時失敗（false negative），降低團隊對 test 的信任。&lt;/p>
&lt;p>常見的 flaky assertion 來源：&lt;/p>
&lt;ul>
&lt;li>依賴 &lt;code>DateTime.now()&lt;/code> 或 &lt;code>stopwatch.elapsed&lt;/code> — 時間精度和系統負載影響結果&lt;/li>
&lt;li>依賴特定的執行順序 — &lt;code>Set&lt;/code> 或 &lt;code>Map&lt;/code> 的迭代順序不保證&lt;/li>
&lt;li>依賴外部服務的回應時間 — 網路延遲導致 timeout&lt;/li>
&lt;/ul>
&lt;h2 id="assertion-改善的操作步驟">Assertion 改善的操作步驟&lt;/h2>
&lt;p>對既有的 test assertion 逐一問三個問題，標記需要改善的：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>行為 check&lt;/strong>：assertion 斷言的是 public API 的回傳值或狀態嗎？如果斷言私有變數或呼叫次數，考慮改成行為斷言。&lt;/li>
&lt;li>&lt;strong>區分 check&lt;/strong>：把 assertion 改成反向（&lt;code>expect(result, 'wrong_value')&lt;/code>），test 會失敗嗎？如果 assertion 太寬鬆（isNotNull、isA），test 可能在錯誤的情況下也通過。&lt;/li>
&lt;li>&lt;strong>穩定 check&lt;/strong>：連續跑 10 次，每次都通過嗎？如果有 flaky，找到依賴的非確定性因素。&lt;/li>
&lt;/ol>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>Flaky test 的系統性根因分類 → &lt;a href="https://tarrragon.github.io/blog/testing/05-test-design-judgment/flaky-test-root-cause/" data-link-title="Flaky test 根因分類" data-link-desc="計時依賴 / 環境差異 / 資源競爭 / 非確定性輸出 — 四類 flaky test 根因的辨識和處理策略">Flaky test 根因分類&lt;/a>&lt;/li>
&lt;li>測試資料的代表性 → &lt;a href="https://tarrragon.github.io/blog/testing/05-test-design-judgment/test-data-representativeness/" data-link-title="Test data 代表性" data-link-desc="手寫 vs 錄製 vs 生成三種測試資料來源 — 測試資料的代表性是一個隱性假設，決定了 test 能發現什麼問題">Test data 代表性&lt;/a>&lt;/li>
&lt;li>Mock 邊界判斷 → &lt;a href="https://tarrragon.github.io/blog/testing/05-test-design-judgment/mock-boundary-decision/" data-link-title="Mock 邊界判斷決策表" data-link-desc="什麼時候 mock 夠用、什麼時候需要真實服務 — 從 API 層 / 協議層 / 環境層的斷裂點判斷 mock 的適用範圍">Mock 邊界判斷決策表&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>Assertion 是 test 的結論 — 「我認為程式碼的行為應該是 X」。Assertion 的品質決定了 test 的有效性：無效的 assertion 讓 test 通過但問題仍在，或讓 test 隨機失敗但問題不在程式碼。</p>
<h2 id="三個判斷問題">三個判斷問題</h2>
<h3 id="斷言的是行為嗎">斷言的是行為嗎</h3>
<p>Assertion 應該斷言程式碼的外部可觀察行為（回傳值、狀態變化、副作用），而非內部實作細節（私有變數的值、呼叫次數、執行順序）。</p>
<p>斷言行為的 test 在重構時不需要改 — 只要行為不變，test 就通過。斷言實作的 test 在任何內部調整時都會壞掉，即使行為完全正確。</p>
<p>例：驗證「parser 正確解析紅色文字」時，斷言 token 的顏色屬性（行為）比斷言 parser 內部的 state machine 走了哪些步驟（實作）更穩定。</p>
<h3 id="能區分正確和錯誤嗎">能區分正確和錯誤嗎</h3>
<p>Assertion 應該在程式碼正確時通過、錯誤時失敗。如果 assertion 無論程式碼正確或錯誤都通過，這個 assertion 沒有提供保護。</p>
<p>常見的無效 assertion：</p>
<p><strong>斷言不為 null</strong>：<code>expect(result, isNotNull)</code> 只驗證「有回傳值」，不驗證「回傳值正確」。回傳錯誤的值也會通過。</p>
<p><strong>斷言型別</strong>：<code>expect(result, isA&lt;List&gt;())</code> 只驗證「回傳 List」，不驗證 List 的內容。空 List 和錯誤內容的 List 都會通過。</p>
<p><strong>斷言包含</strong>：<code>expect(result, contains('error'))</code> 驗證字串包含 &rsquo;error&rsquo;，但如果回傳 &rsquo;no error occurred&rsquo;（正確情境）也包含 &rsquo;error&rsquo; — assertion 無法區分正確和錯誤。</p>
<p>T.C3 的 parser test 斷言 <code>expect(tokens.first, isA&lt;TextToken&gt;())</code> — 驗證 token 型別是 TextToken。但正確解析和透傳亂碼都可能產生 TextToken，assertion 無法區分（本章合成，TF-5 Derive — 透傳的靜默副作用和 assertion 的區分力有 tension）。</p>
<h3 id="會-flaky-嗎">會 flaky 嗎</h3>
<p>Assertion 是否依賴非確定性因素 — 時間、隨機數、外部服務狀態、執行順序。如果是，test 可能在程式碼正確時失敗（false negative），降低團隊對 test 的信任。</p>
<p>常見的 flaky assertion 來源：</p>
<ul>
<li>依賴 <code>DateTime.now()</code> 或 <code>stopwatch.elapsed</code> — 時間精度和系統負載影響結果</li>
<li>依賴特定的執行順序 — <code>Set</code> 或 <code>Map</code> 的迭代順序不保證</li>
<li>依賴外部服務的回應時間 — 網路延遲導致 timeout</li>
</ul>
<h2 id="assertion-改善的操作步驟">Assertion 改善的操作步驟</h2>
<p>對既有的 test assertion 逐一問三個問題，標記需要改善的：</p>
<ol>
<li><strong>行為 check</strong>：assertion 斷言的是 public API 的回傳值或狀態嗎？如果斷言私有變數或呼叫次數，考慮改成行為斷言。</li>
<li><strong>區分 check</strong>：把 assertion 改成反向（<code>expect(result, 'wrong_value')</code>），test 會失敗嗎？如果 assertion 太寬鬆（isNotNull、isA），test 可能在錯誤的情況下也通過。</li>
<li><strong>穩定 check</strong>：連續跑 10 次，每次都通過嗎？如果有 flaky，找到依賴的非確定性因素。</li>
</ol>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>Flaky test 的系統性根因分類 → <a href="/blog/testing/05-test-design-judgment/flaky-test-root-cause/" data-link-title="Flaky test 根因分類" data-link-desc="計時依賴 / 環境差異 / 資源競爭 / 非確定性輸出 — 四類 flaky test 根因的辨識和處理策略">Flaky test 根因分類</a></li>
<li>測試資料的代表性 → <a href="/blog/testing/05-test-design-judgment/test-data-representativeness/" data-link-title="Test data 代表性" data-link-desc="手寫 vs 錄製 vs 生成三種測試資料來源 — 測試資料的代表性是一個隱性假設，決定了 test 能發現什麼問題">Test data 代表性</a></li>
<li>Mock 邊界判斷 → <a href="/blog/testing/05-test-design-judgment/mock-boundary-decision/" data-link-title="Mock 邊界判斷決策表" data-link-desc="什麼時候 mock 夠用、什麼時候需要真實服務 — 從 API 層 / 協議層 / 環境層的斷裂點判斷 mock 的適用範圍">Mock 邊界判斷決策表</a></li>
</ul>
]]></content:encoded></item><item><title>模組五：測試設計判斷</title><link>https://tarrragon.github.io/blog/testing/05-test-design-judgment/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/testing/05-test-design-judgment/</guid><description>&lt;p>回答「這個斷言該怎麼寫」「這個 mock 邊界對嗎」。&lt;/p>
&lt;h2 id="對應-findings">對應 findings&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Finding&lt;/th>
 &lt;th>來源&lt;/th>
 &lt;th>內容&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>TF-4&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/testing/cases/ansi-parser-test-data-blindspot/" data-link-title="T.C3 ANSI parser 測試資料不覆蓋真實 shell output" data-link-desc="ANSI parser 只處理基本 SGR 色彩碼、unit test 用手寫乾淨字串驗證 — 真實 zsh prompt 送出 OSC 標題設定、CSI private mode 游標隱藏、括號貼上模式等數十種控制序列，全部殘留為亂碼">T.C3&lt;/a>&lt;/td>
 &lt;td>手寫測試資料是真實環境的乾淨子集&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>TF-5&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/testing/cases/ansi-parser-test-data-blindspot/" data-link-title="T.C3 ANSI parser 測試資料不覆蓋真實 shell output" data-link-desc="ANSI parser 只處理基本 SGR 色彩碼、unit test 用手寫乾淨字串驗證 — 真實 zsh prompt 送出 OSC 標題設定、CSI private mode 游標隱藏、括號貼上模式等數十種控制序列，全部殘留為亂碼">T.C3&lt;/a>&lt;/td>
 &lt;td>Parser 透傳未知序列的靜默副作用&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="待寫章節">待寫章節&lt;/h2>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Mock 邊界判斷決策表（什麼時候 mock 夠、什麼時候需要 real）&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Test data 代表性（手寫 vs 錄製 vs 生成）&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Assertion 品質三問（斷言的是行為嗎？能區分正確和錯誤嗎？會 flaky 嗎？）&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Flaky test 根因分類（計時依賴 / 環境差異 / 資源競爭 / 非確定性）&lt;/li>
&lt;/ul>
&lt;h2 id="跨分類引用">跨分類引用&lt;/h2>
&lt;ul>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/monitoring/05-platform-adaptation/" data-link-title="模組五：平台適配" data-link-desc="JS CORS / Flutter isolate / Python GIL / Go graceful shutdown — 各平台的特殊考量">monitoring 模組五 平台適配&lt;/a>：各平台的 error 攔截機制差異影響 test 設計&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>回答「這個斷言該怎麼寫」「這個 mock 邊界對嗎」。</p>
<h2 id="對應-findings">對應 findings</h2>
<table>
  <thead>
      <tr>
          <th>Finding</th>
          <th>來源</th>
          <th>內容</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>TF-4</td>
          <td><a href="/blog/testing/cases/ansi-parser-test-data-blindspot/" data-link-title="T.C3 ANSI parser 測試資料不覆蓋真實 shell output" data-link-desc="ANSI parser 只處理基本 SGR 色彩碼、unit test 用手寫乾淨字串驗證 — 真實 zsh prompt 送出 OSC 標題設定、CSI private mode 游標隱藏、括號貼上模式等數十種控制序列，全部殘留為亂碼">T.C3</a></td>
          <td>手寫測試資料是真實環境的乾淨子集</td>
      </tr>
      <tr>
          <td>TF-5</td>
          <td><a href="/blog/testing/cases/ansi-parser-test-data-blindspot/" data-link-title="T.C3 ANSI parser 測試資料不覆蓋真實 shell output" data-link-desc="ANSI parser 只處理基本 SGR 色彩碼、unit test 用手寫乾淨字串驗證 — 真實 zsh prompt 送出 OSC 標題設定、CSI private mode 游標隱藏、括號貼上模式等數十種控制序列，全部殘留為亂碼">T.C3</a></td>
          <td>Parser 透傳未知序列的靜默副作用</td>
      </tr>
  </tbody>
</table>
<h2 id="待寫章節">待寫章節</h2>
<ul>
<li><input checked="" disabled="" type="checkbox"> Mock 邊界判斷決策表（什麼時候 mock 夠、什麼時候需要 real）</li>
<li><input checked="" disabled="" type="checkbox"> Test data 代表性（手寫 vs 錄製 vs 生成）</li>
<li><input checked="" disabled="" type="checkbox"> Assertion 品質三問（斷言的是行為嗎？能區分正確和錯誤嗎？會 flaky 嗎？）</li>
<li><input checked="" disabled="" type="checkbox"> Flaky test 根因分類（計時依賴 / 環境差異 / 資源競爭 / 非確定性）</li>
</ul>
<h2 id="跨分類引用">跨分類引用</h2>
<ul>
<li>→ <a href="/blog/monitoring/05-platform-adaptation/" data-link-title="模組五：平台適配" data-link-desc="JS CORS / Flutter isolate / Python GIL / Go graceful shutdown — 各平台的特殊考量">monitoring 模組五 平台適配</a>：各平台的 error 攔截機制差異影響 test 設計</li>
</ul>
]]></content:encoded></item></channel></rss>