<?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>Race-Condition on Tarragon</title><link>https://tarrragon.github.io/blog/tags/race-condition/</link><description>Recent content in Race-Condition 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/race-condition/index.xml" rel="self" type="application/rss+xml"/><item><title>T.C8 fire-and-forget 編排讓測試單跑綠、合跑紅</title><link>https://tarrragon.github.io/blog/testing/cases/fire-and-forget-test-race/</link><pubDate>Fri, 17 Jul 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/testing/cases/fire-and-forget-test-race/</guid><description>&lt;p>flaky 名單上反覆出現的一類：單獨執行綠燈、排進整批就紅燈的測試。這類 flaky 的根因：&lt;strong>被測編排本身是 &lt;a href="https://tarrragon.github.io/blog/testing/knowledge-cards/fire-and-forget-orchestration/" data-link-title="Fire-and-Forget Orchestration（射後不理編排）" data-link-desc="呼叫後不等待完成的編排形態；產生「方法返回不等於流程完成」的時序落差，是 flaky test 的常見根因之一">fire-and-forget&lt;/a>（呼叫後不等待完成），&lt;a href="https://tarrragon.github.io/blog/testing/knowledge-cards/flow-test/" data-link-title="Flow Test（流程測試）" data-link-desc="在假後端上驅動真實前端服務鏈、斷言散佈於業務旅程各階段的測試形態；與 unit / integration / E2E 的邊界劃分">流程測試&lt;/a>的斷言在與它賽跑&lt;/strong>。競態本身也是資訊——它揭露了產品程式的時序特性；下文示範怎麼把這份資訊留下來。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>結帳流程的收尾是一串背景動作：列印收據、同步資料、清理狀態、通知外接螢幕。主方法送出結帳請求成功後呼叫這個收尾編排，但&lt;strong>沒有 await&lt;/strong>——主方法立刻返回，收尾在背景繼續。&lt;/p>
&lt;p>流程測試在 &lt;code>結帳()&lt;/code> 返回後立刻斷言「收據印了一張、結帳狀態已清除」。單獨執行這個測試檔：綠。與其他測試檔一起執行：紅——事件迴圈的排程不同，斷言跑在收尾完成之前。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>指標&lt;/th>
 &lt;th>值&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>症狀&lt;/td>
 &lt;td>同一條測試單跑綠、合跑紅（典型 flaky 特徵）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>根因&lt;/td>
 &lt;td>被測編排 fire-and-forget，斷言與背景收尾賽跑&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>修法&lt;/td>
 &lt;td>斷言前輪詢等待終態（狀態已清除且列印計數到位），設上限次數&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>附帶發現&lt;/td>
 &lt;td>設計事實記錄備查：產品上「結帳成功提示」出現時，列印與同步仍在進行&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>flaky 的第一個提問：測試在等什麼、程式承諾了什麼&lt;/strong>。這條測試假設「方法返回＝流程完成」，但程式的實際承諾是「方法返回＝請求已送出」。斷言超出了被測物的承諾範圍，紅綠自然隨排程漂移。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>輪詢終態而非固定延遲&lt;/strong>。加一個固定 sleep 能讓測試變綠，但那是把賽跑的起跑線挪後——排程更慢的環境（CI、低階機器）照樣輸。正確做法是輪詢「可觀察的終態」（狀態欄位、計數器），到達即斷言、超過上限次數才失敗。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>競態是資訊，不只是麻煩&lt;/strong>。測試逼問出「這個方法返回時什麼還沒完成」——這個答案對產品同樣重要：如果日後有功能要在結帳後立刻依賴收尾結果（例如立刻讀取列印狀態），這裡就是陷阱。把它記錄下來，比默默繞過有價值。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="策略">策略&lt;/h2>
&lt;ol>
&lt;li>&lt;strong>對 fire-and-forget 編排的測試，一律等待終態&lt;/strong>：明確列出「完成」的可觀察條件，輪詢直到滿足或超時。定界原則：上限取預期收尾時間的數倍量級、輪詢間隔遠小於上限——上限太短重回 flaky、太長把真 bug 等過去。&lt;/li>
&lt;li>&lt;strong>分辨兩種修法的適用性&lt;/strong>：能改產品（把收尾改成可等待）就改產品；不能或不該改（fire-and-forget 是刻意的 UX 決策——先解鎖畫面）就改測試的等待策略，並把時序特性記錄在案。&lt;/li>
&lt;li>&lt;strong>flaky 診斷從「單跑 vs 合跑」開始&lt;/strong>：單跑綠合跑紅幾乎必然是共享狀態或時序競態，優先檢查未等待的非同步鏈。&lt;/li>
&lt;/ol>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>flaky 根因的系統分類 → &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/01-test-strategy-layers/semantic-fake-backend/" data-link-title="語意級假後端與流程測試" data-link-desc="bug 的成因是對後端行為的假設錯誤、由測試餵資料的 stub 驗證不出來時：建一個持有狀態、模擬已證實後端行為的假後端（test double 分類的 fake），讓流程測試走完整的多服務互動鏈">語意級假後端與流程測試&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>flaky 名單上反覆出現的一類：單獨執行綠燈、排進整批就紅燈的測試。這類 flaky 的根因：<strong>被測編排本身是 <a href="/blog/testing/knowledge-cards/fire-and-forget-orchestration/" data-link-title="Fire-and-Forget Orchestration（射後不理編排）" data-link-desc="呼叫後不等待完成的編排形態；產生「方法返回不等於流程完成」的時序落差，是 flaky test 的常見根因之一">fire-and-forget</a>（呼叫後不等待完成），<a href="/blog/testing/knowledge-cards/flow-test/" data-link-title="Flow Test（流程測試）" data-link-desc="在假後端上驅動真實前端服務鏈、斷言散佈於業務旅程各階段的測試形態；與 unit / integration / E2E 的邊界劃分">流程測試</a>的斷言在與它賽跑</strong>。競態本身也是資訊——它揭露了產品程式的時序特性；下文示範怎麼把這份資訊留下來。</p>
<h2 id="觀察">觀察</h2>
<p>結帳流程的收尾是一串背景動作：列印收據、同步資料、清理狀態、通知外接螢幕。主方法送出結帳請求成功後呼叫這個收尾編排，但<strong>沒有 await</strong>——主方法立刻返回，收尾在背景繼續。</p>
<p>流程測試在 <code>結帳()</code> 返回後立刻斷言「收據印了一張、結帳狀態已清除」。單獨執行這個測試檔：綠。與其他測試檔一起執行：紅——事件迴圈的排程不同，斷言跑在收尾完成之前。</p>
<table>
  <thead>
      <tr>
          <th>指標</th>
          <th>值</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>症狀</td>
          <td>同一條測試單跑綠、合跑紅（典型 flaky 特徵）</td>
      </tr>
      <tr>
          <td>根因</td>
          <td>被測編排 fire-and-forget，斷言與背景收尾賽跑</td>
      </tr>
      <tr>
          <td>修法</td>
          <td>斷言前輪詢等待終態（狀態已清除且列印計數到位），設上限次數</td>
      </tr>
      <tr>
          <td>附帶發現</td>
          <td>設計事實記錄備查：產品上「結帳成功提示」出現時，列印與同步仍在進行</td>
      </tr>
  </tbody>
</table>
<h2 id="判讀">判讀</h2>
<ol>
<li>
<p><strong>flaky 的第一個提問：測試在等什麼、程式承諾了什麼</strong>。這條測試假設「方法返回＝流程完成」，但程式的實際承諾是「方法返回＝請求已送出」。斷言超出了被測物的承諾範圍，紅綠自然隨排程漂移。</p>
</li>
<li>
<p><strong>輪詢終態而非固定延遲</strong>。加一個固定 sleep 能讓測試變綠，但那是把賽跑的起跑線挪後——排程更慢的環境（CI、低階機器）照樣輸。正確做法是輪詢「可觀察的終態」（狀態欄位、計數器），到達即斷言、超過上限次數才失敗。</p>
</li>
<li>
<p><strong>競態是資訊，不只是麻煩</strong>。測試逼問出「這個方法返回時什麼還沒完成」——這個答案對產品同樣重要：如果日後有功能要在結帳後立刻依賴收尾結果（例如立刻讀取列印狀態），這裡就是陷阱。把它記錄下來，比默默繞過有價值。</p>
</li>
</ol>
<h2 id="策略">策略</h2>
<ol>
<li><strong>對 fire-and-forget 編排的測試，一律等待終態</strong>：明確列出「完成」的可觀察條件，輪詢直到滿足或超時。定界原則：上限取預期收尾時間的數倍量級、輪詢間隔遠小於上限——上限太短重回 flaky、太長把真 bug 等過去。</li>
<li><strong>分辨兩種修法的適用性</strong>：能改產品（把收尾改成可等待）就改產品；不能或不該改（fire-and-forget 是刻意的 UX 決策——先解鎖畫面）就改測試的等待策略，並把時序特性記錄在案。</li>
<li><strong>flaky 診斷從「單跑 vs 合跑」開始</strong>：單跑綠合跑紅幾乎必然是共享狀態或時序競態，優先檢查未等待的非同步鏈。</li>
</ol>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>flaky 根因的系統分類 → <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/01-test-strategy-layers/semantic-fake-backend/" data-link-title="語意級假後端與流程測試" data-link-desc="bug 的成因是對後端行為的假設錯誤、由測試餵資料的 stub 驗證不出來時：建一個持有狀態、模擬已證實後端行為的假後端（test double 分類的 fake），讓流程測試走完整的多服務互動鏈">語意級假後端與流程測試</a></li>
</ul>
]]></content:encoded></item></channel></rss>