<?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>Simulator on Tarragon</title><link>https://tarrragon.github.io/blog/tags/simulator/</link><description>Recent content in Simulator 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/simulator/index.xml" rel="self" type="application/rss+xml"/><item><title>開發環境 vs 真機的 gate 行為差異表</title><link>https://tarrragon.github.io/blog/ux-design/02-gate-fallback/dev-vs-real-gate-behavior/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ux-design/02-gate-fallback/dev-vs-real-gate-behavior/</guid><description>&lt;p>開發環境遮蔽 gate 問題的機制是：模擬器或 debug build 中的 gate 行為比真機寬鬆，讓問題在開發階段不可見，直到實機測試或 production 才浮現。這和 mock 遮蔽 protocol 問題的機制結構相同（&lt;a href="https://tarrragon.github.io/blog/testing/01-test-strategy-layers/" data-link-title="模組一：測試策略分層" data-link-desc="Unit / Protocol Integration / Screen State 三層測試各自的職責、盲區和判斷原則">testing 模組一&lt;/a>）— 開發環境的「寬鬆模式」讓功能缺失變得不可見。&lt;/p>
&lt;h2 id="差異機制">差異機制&lt;/h2>
&lt;h3 id="模擬器不支援硬體功能">模擬器不支援硬體功能&lt;/h3>
&lt;p>iOS 模擬器不支援 Face ID / Touch ID 硬體。&lt;code>local_auth&lt;/code> 的 &lt;code>isAvailable()&lt;/code> 在模擬器上回傳 &lt;code>false&lt;/code>（&lt;code>isDeviceSupported()&lt;/code> 為 &lt;code>true&lt;/code> 但 &lt;code>getAvailableBiometrics()&lt;/code> 為空），app 跳過認證走預設路徑。&lt;/p>
&lt;p>在真機上 &lt;code>isAvailable()&lt;/code> 回傳 &lt;code>true&lt;/code>，app 嘗試認證，如果設定了 &lt;code>biometricOnly: true&lt;/code> 且 Face ID 失敗，使用者被擋住。模擬器上「跳過認證直接使用」的體驗讓開發者以為認證流程沒有問題（&lt;a href="https://tarrragon.github.io/blog/ux-design/cases/biometric-only-no-fallback/" data-link-title="U.C2 biometricOnly=true 無密碼 fallback" data-link-desc="Flutter app 的生物辨識設定 biometricOnly: true 阻擋所有非生物辨識認證方式 — Face ID 不可用時使用者直接被擋住，沒有替代路徑">U.C2&lt;/a>）。&lt;/p>
&lt;h3 id="debug-build-的權限行為不同">Debug build 的權限行為不同&lt;/h3>
&lt;p>某些平台在 debug build 和 release build 的權限處理不同。例如 Android 的某些 OEM 客製化系統在 debug mode 下自動授予特定權限，release mode 下需要手動授權。&lt;/p>
&lt;h3 id="test-環境跳過-gate">Test 環境跳過 gate&lt;/h3>
&lt;p>Unit test 和 integration test 通常 mock 掉所有 gate — &lt;code>FakeBiometricService&lt;/code> 永遠回傳成功，&lt;code>FakeNetworkChecker&lt;/code> 永遠回傳已連線。這和名義 integration test 的問題相同 — test 環境的「一切正常」遮蔽了真實環境的 gate 失敗場景。&lt;/p>
&lt;h2 id="gate-行為差異表">Gate 行為差異表&lt;/h2>
&lt;p>在功能規格中建立一張差異表，列出每個 gate 在不同環境下的行為差異：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Gate&lt;/th>
 &lt;th>模擬器行為&lt;/th>
 &lt;th>真機 debug&lt;/th>
 &lt;th>真機 release&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;td>正常&lt;/td>
 &lt;td>模擬器上看不到 fallback 缺失&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>網路連線&lt;/td>
 &lt;td>通常正常（host 網路）&lt;/td>
 &lt;td>可斷 WiFi 測試&lt;/td>
 &lt;td>行動網路 + WiFi&lt;/td>
 &lt;td>模擬器的網路狀態不代表行動網路&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>相機權限&lt;/td>
 &lt;td>無相機（或虛擬相機）&lt;/td>
 &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;td>正常&lt;/td>
 &lt;td>模擬器完全跳過藍牙相關功能&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Push 通知&lt;/td>
 &lt;td>不支援（iOS 模擬器）&lt;/td>
 &lt;td>可測試&lt;/td>
 &lt;td>正常&lt;/td>
 &lt;td>通知觸發的導航路徑在模擬器不可測&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>App 簽名驗證&lt;/td>
 &lt;td>debug 簽名自動通過&lt;/td>
 &lt;td>debug 簽名&lt;/td>
 &lt;td>release 簽名&lt;/td>
 &lt;td>簽名相關的 gate 只在 release 生效&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="差異表的使用方式">差異表的使用方式&lt;/h2>
&lt;h3 id="開發階段">開發階段&lt;/h3>
&lt;p>開發者對照差異表，意識到哪些 gate 在當前環境下沒有被真實驗證。差異表中「模擬器行為」和「真機 release」不同的行 = 需要上真機確認的項目。&lt;/p>
&lt;h3 id="實機測試規劃">實機測試規劃&lt;/h3>
&lt;p>測試計畫中針對差異表的每一行設計測試案例。生物辨識的測試案例必須涵蓋「Face ID 失敗時的 fallback」，網路連線的測試案例必須涵蓋「飛航模式下的 UX」。&lt;/p>
&lt;h3 id="code-review">Code review&lt;/h3>
&lt;p>Review 涉及 gate 的程式碼時，對照差異表確認 fallback 路徑是否存在。如果 review 的程式碼用了 &lt;code>biometricOnly: true&lt;/code>，差異表立刻提示「模擬器上看不到這個問題，需要上真機確認 fallback」。&lt;/p>
&lt;p>差異表揭露的問題和 testing 領域的 mock 遮蔽在結構上相同 — &lt;a href="https://tarrragon.github.io/blog/testing/01-test-strategy-layers/mock-masking-mechanism/" data-link-title="Mock 遮蔽機制分析" data-link-desc="Mock 在 API 層、協議層、環境層之間製造的結構性盲區 — 斷裂點在哪、為什麼 mock 無法也不應該模擬協議行為">testing 模組一 Mock 遮蔽機制&lt;/a>從 API 層 vs 協議層的角度分析同一類問題。差異表本身是&lt;a href="https://tarrragon.github.io/blog/ux-design/02-gate-fallback/gate-three-questions/" data-link-title="Gate 分類與三問設計法" data-link-desc="每個 gate 設計時問三個問題：成功時做什麼、失敗時做什麼、使用者不知道發生什麼時做什麼">三問設計法&lt;/a>在實機驗證階段的延伸，biometric gate 的完整 fallback 設計見 &lt;a href="https://tarrragon.github.io/blog/ux-design/02-gate-fallback/biometric-fallback-design/" data-link-title="Biometric fallback 完整設計" data-link-desc="iOS Face ID / Touch ID 和 Android BiometricPrompt 的行為差異、fallback 策略、安全 vs 可用性取捨的顯式記錄方法">Biometric fallback 完整設計&lt;/a>。&lt;/p></description><content:encoded><![CDATA[<p>開發環境遮蔽 gate 問題的機制是：模擬器或 debug build 中的 gate 行為比真機寬鬆，讓問題在開發階段不可見，直到實機測試或 production 才浮現。這和 mock 遮蔽 protocol 問題的機制結構相同（<a href="/blog/testing/01-test-strategy-layers/" data-link-title="模組一：測試策略分層" data-link-desc="Unit / Protocol Integration / Screen State 三層測試各自的職責、盲區和判斷原則">testing 模組一</a>）— 開發環境的「寬鬆模式」讓功能缺失變得不可見。</p>
<h2 id="差異機制">差異機制</h2>
<h3 id="模擬器不支援硬體功能">模擬器不支援硬體功能</h3>
<p>iOS 模擬器不支援 Face ID / Touch ID 硬體。<code>local_auth</code> 的 <code>isAvailable()</code> 在模擬器上回傳 <code>false</code>（<code>isDeviceSupported()</code> 為 <code>true</code> 但 <code>getAvailableBiometrics()</code> 為空），app 跳過認證走預設路徑。</p>
<p>在真機上 <code>isAvailable()</code> 回傳 <code>true</code>，app 嘗試認證，如果設定了 <code>biometricOnly: true</code> 且 Face ID 失敗，使用者被擋住。模擬器上「跳過認證直接使用」的體驗讓開發者以為認證流程沒有問題（<a href="/blog/ux-design/cases/biometric-only-no-fallback/" data-link-title="U.C2 biometricOnly=true 無密碼 fallback" data-link-desc="Flutter app 的生物辨識設定 biometricOnly: true 阻擋所有非生物辨識認證方式 — Face ID 不可用時使用者直接被擋住，沒有替代路徑">U.C2</a>）。</p>
<h3 id="debug-build-的權限行為不同">Debug build 的權限行為不同</h3>
<p>某些平台在 debug build 和 release build 的權限處理不同。例如 Android 的某些 OEM 客製化系統在 debug mode 下自動授予特定權限，release mode 下需要手動授權。</p>
<h3 id="test-環境跳過-gate">Test 環境跳過 gate</h3>
<p>Unit test 和 integration test 通常 mock 掉所有 gate — <code>FakeBiometricService</code> 永遠回傳成功，<code>FakeNetworkChecker</code> 永遠回傳已連線。這和名義 integration test 的問題相同 — test 環境的「一切正常」遮蔽了真實環境的 gate 失敗場景。</p>
<h2 id="gate-行為差異表">Gate 行為差異表</h2>
<p>在功能規格中建立一張差異表，列出每個 gate 在不同環境下的行為差異：</p>
<table>
  <thead>
      <tr>
          <th>Gate</th>
          <th>模擬器行為</th>
          <th>真機 debug</th>
          <th>真機 release</th>
          <th>風險</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>生物辨識</td>
          <td>跳過（硬體不可用）</td>
          <td>可測試（需設定）</td>
          <td>正常</td>
          <td>模擬器上看不到 fallback 缺失</td>
      </tr>
      <tr>
          <td>網路連線</td>
          <td>通常正常（host 網路）</td>
          <td>可斷 WiFi 測試</td>
          <td>行動網路 + WiFi</td>
          <td>模擬器的網路狀態不代表行動網路</td>
      </tr>
      <tr>
          <td>相機權限</td>
          <td>無相機（或虛擬相機）</td>
          <td>可測試</td>
          <td>正常</td>
          <td>模擬器無法測試真實權限流程</td>
      </tr>
      <tr>
          <td>藍牙</td>
          <td>不支援</td>
          <td>可測試</td>
          <td>正常</td>
          <td>模擬器完全跳過藍牙相關功能</td>
      </tr>
      <tr>
          <td>Push 通知</td>
          <td>不支援（iOS 模擬器）</td>
          <td>可測試</td>
          <td>正常</td>
          <td>通知觸發的導航路徑在模擬器不可測</td>
      </tr>
      <tr>
          <td>App 簽名驗證</td>
          <td>debug 簽名自動通過</td>
          <td>debug 簽名</td>
          <td>release 簽名</td>
          <td>簽名相關的 gate 只在 release 生效</td>
      </tr>
  </tbody>
</table>
<h2 id="差異表的使用方式">差異表的使用方式</h2>
<h3 id="開發階段">開發階段</h3>
<p>開發者對照差異表，意識到哪些 gate 在當前環境下沒有被真實驗證。差異表中「模擬器行為」和「真機 release」不同的行 = 需要上真機確認的項目。</p>
<h3 id="實機測試規劃">實機測試規劃</h3>
<p>測試計畫中針對差異表的每一行設計測試案例。生物辨識的測試案例必須涵蓋「Face ID 失敗時的 fallback」，網路連線的測試案例必須涵蓋「飛航模式下的 UX」。</p>
<h3 id="code-review">Code review</h3>
<p>Review 涉及 gate 的程式碼時，對照差異表確認 fallback 路徑是否存在。如果 review 的程式碼用了 <code>biometricOnly: true</code>，差異表立刻提示「模擬器上看不到這個問題，需要上真機確認 fallback」。</p>
<p>差異表揭露的問題和 testing 領域的 mock 遮蔽在結構上相同 — <a href="/blog/testing/01-test-strategy-layers/mock-masking-mechanism/" data-link-title="Mock 遮蔽機制分析" data-link-desc="Mock 在 API 層、協議層、環境層之間製造的結構性盲區 — 斷裂點在哪、為什麼 mock 無法也不應該模擬協議行為">testing 模組一 Mock 遮蔽機制</a>從 API 層 vs 協議層的角度分析同一類問題。差異表本身是<a href="/blog/ux-design/02-gate-fallback/gate-three-questions/" data-link-title="Gate 分類與三問設計法" data-link-desc="每個 gate 設計時問三個問題：成功時做什麼、失敗時做什麼、使用者不知道發生什麼時做什麼">三問設計法</a>在實機驗證階段的延伸，biometric gate 的完整 fallback 設計見 <a href="/blog/ux-design/02-gate-fallback/biometric-fallback-design/" data-link-title="Biometric fallback 完整設計" data-link-desc="iOS Face ID / Touch ID 和 Android BiometricPrompt 的行為差異、fallback 策略、安全 vs 可用性取捨的顯式記錄方法">Biometric fallback 完整設計</a>。</p>
]]></content:encoded></item></channel></rss>