<?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>Isp on Tarragon</title><link>https://tarrragon.github.io/blog/tags/isp/</link><description>Recent content in Isp on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Fri, 10 Jul 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/isp/index.xml" rel="self" type="application/rss+xml"/><item><title>mock 要配置 55 個方法、實際只用 5 個 — 測試痛是介面設計痛的探針</title><link>https://tarrragon.github.io/blog/work-log/flutter_port_interface_mock_hell_isp/</link><pubDate>Fri, 10 Jul 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/work-log/flutter_port_interface_mock_hell_isp/</guid><description>&lt;blockquote>
&lt;p>&lt;strong>觸發場景&lt;/strong>：Flutter 書籍管理 App 要幫 &lt;code>SyncReadinessService&lt;/code> 寫測試，mock 設置一路撞牆：四個依賴共 55 個方法要配置、漏配就 MissingStubError、mockito 的嵌套 when 又有語法限制——而這個 service 實際呼叫的方法只有 5 個
&lt;strong>疑問來源&lt;/strong>：測試這麼難寫，是測試工具的問題、還是被測物的問題？
&lt;strong>整理目的&lt;/strong>：記下「mock 負擔」作為介面設計探針的判讀方式、以及 Port 介面（ISP）的落地步驟
&lt;strong>本文邊界&lt;/strong>：素材是該專案 v0.18.6 的重構計畫（含依賴盤點數字與 wave 拆分）；Port 是 hexagonal architecture 的用語、這裡取其「消費端定義的窄介面」語意&lt;/p>&lt;/blockquote>
&lt;hr>
&lt;h2 id="依賴盤點91-的-mock-配置是浪費">依賴盤點：91% 的 mock 配置是浪費&lt;/h2>
&lt;p>寫不動測試的第一步是把依賴攤開來數。&lt;code>SyncReadinessService&lt;/code> 的建構子收四個依賴，逐一盤點方法數與實際使用：&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;code>SyncRepository&lt;/code>&lt;/td>
 &lt;td>23&lt;/td>
 &lt;td>2（待同步變更、待解衝突）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>BookRepository&lt;/code>&lt;/td>
 &lt;td>16&lt;/td>
 &lt;td>1（getAllBooks）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>ChangeTracker&lt;/code>&lt;/td>
 &lt;td>10&lt;/td>
 &lt;td>&lt;strong>0&lt;/strong>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>EventBus&lt;/code>&lt;/td>
 &lt;td>6&lt;/td>
 &lt;td>2&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>合計 55 個方法、用 5 個。mock 這個 service 的依賴，等於為 50 個永遠不會被呼叫的方法做配置決策——每個都可能漏（MissingStubError）、每個都是測試檔的噪音。&lt;code>ChangeTracker&lt;/code> 更直接：10 個方法、零使用，這個依賴純粹是建構子沿著前例複製來的。&lt;/p>
&lt;p>數字本身就是診斷：&lt;strong>mock 負擔正比於介面寬度、不正比於被測物的複雜度&lt;/strong>。測試難寫的根因不在 mockito、在被測物宣告的依賴遠寬於它需要的能力。&lt;/p>
&lt;h2 id="往上追一個-repository五種職責">往上追：一個 Repository、五種職責&lt;/h2>
&lt;p>&lt;code>SyncRepository&lt;/code> 的 23 個方法拆開看是五種職責：變更記錄管理（6）、同步任務管理（6、預留給 UC-08）、衝突解決（5）、離線佇列（4、預留 UC-09）、同步統計（2、預留 UC-10）。五分之三是&lt;strong>為未來 use case 預留的投機式方法&lt;/strong>——現在沒有人呼叫、但每個消費者都被迫認識它們。&lt;/p>
&lt;p>這是介面隔離原則（ISP）教科書式的違反現場，而它的第一個受害者是測試：生產程式碼呼叫方法時不在乎介面還有幾個方法、mock 卻要面對整個介面。&lt;strong>測試是第一個被迫「完整消費」介面的客戶&lt;/strong>，所以介面過寬的痛總是先在測試爆。&lt;/p>
&lt;h2 id="修法消費端定義的窄介面">修法：消費端定義的窄介面&lt;/h2>
&lt;p>重構的核心動作是抽 Port——從消費者的實際需求出發定義介面、而不是從資料來源的能力出發：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-dart" data-lang="dart">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1">/// 從 SyncRepository 的 23 個方法中抽取實際需要的 2 個
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">abstract&lt;/span> &lt;span class="kd">class&lt;/span> &lt;span class="nc">SyncQueryPort&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="n">Future&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">List&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">ChangeRecord&lt;/span>&lt;span class="o">&amp;gt;&amp;gt;&lt;/span> &lt;span class="n">getPendingChanges&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="o">?&lt;/span> &lt;span class="n">limit&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 class="n">Future&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">List&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">ConflictResolution&lt;/span>&lt;span class="o">&amp;gt;&amp;gt;&lt;/span> &lt;span class="n">getPendingConflicts&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="o">?&lt;/span> &lt;span class="n">limit&lt;/span>&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&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>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="c1">/// 從 BookRepository 的 16 個方法中抽取實際需要的 1 個
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">abstract&lt;/span> &lt;span class="kd">class&lt;/span> &lt;span class="nc">BookQueryPort&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> &lt;span class="n">Future&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">List&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Book&lt;/span>&lt;span class="o">&amp;gt;&amp;gt;&lt;/span> &lt;span class="n">getAllBooks&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>三個配套讓改動保持小：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Repository 實作 Port、原介面不動&lt;/strong>：&lt;code>abstract class SyncRepository implements SyncQueryPort&lt;/code>——既有實作自動滿足新介面、其他消費者不受影響&lt;/li>
&lt;li>&lt;strong>Service 建構子改收 Port&lt;/strong>：依賴從 55 個方法縮到 5 個、&lt;code>ChangeTracker&lt;/code> 直接移除；測試 mock 的對象變成 2 方法與 1 方法的小介面&lt;/li>
&lt;li>&lt;strong>預留方法標記啟用時機&lt;/strong>：&lt;code>// TODO(UC-08): 實際同步功能時啟用&lt;/code>——投機式方法不刪（設計已評估過）、但每個都有名字跟啟用條件，下次盤點時「這是預留還是死碼」有據可查&lt;/li>
&lt;/ul>
&lt;p>值得注意方向性：Port 放在&lt;strong>消費端的 domain 目錄&lt;/strong>（&lt;code>synchronization/ports/&lt;/code>、&lt;code>library/ports/&lt;/code>），因為它表達的是「這個 domain 需要什麼能力」、不是「Repository 提供什麼」。同一個 Repository 未來可以實作多個不同消費者的 Port，各自窄、互不牽連。&lt;/p>
&lt;h2 id="判讀徵兆">判讀徵兆&lt;/h2>
&lt;ul>
&lt;li>mock 設置的行數超過測試本體——先數被測物依賴的介面方法數 vs 實際呼叫數&lt;/li>
&lt;li>MissingStubError 反覆出現——每一次都是「介面要求你認識的方法」跟「你實際關心的方法」的差距&lt;/li>
&lt;li>建構子的某個依賴在整個 class 內零呼叫——複製前例的沉積、直接刪&lt;/li>
&lt;li>介面裡一半以上的方法標著「未來會用」——預留可以，但要有 TODO 加啟用條件，否則每個消費者與 mock 永遠陪葬&lt;/li>
&lt;/ul>
&lt;p>「測試很難寫」在這個 case 裡是禮物：它比任何架構審查都早、都具體地量化了介面設計的問題（91% 這個數字就是證據）。把測試痛當成噪音硬吞（寫更肥的 mock helper）、跟把它當探針回頭修介面，是兩條分岔路——同專案的 &lt;a href="https://tarrragon.github.io/blog/work-log/flutter_mock_infrastructure_overengineering_deleted/" data-link-title="1101 行自建測試基礎設施、重構刪掉 82.5% — 過度工程的三種形態" data-link-desc="自建測試基礎設施前先問框架的標準做法是什麼：mock 純資料物件、helper 帶並發鎖與記憶體洩漏防護、mock 放進 lib/ 進生產依賴圖，三種形態都在重新發明 Riverpod overrideWith 一行就有的東西。精緻的設計文件不是價值證明——它可以精心規劃一個不需要存在的系統。">mock 基礎設施過度工程&lt;/a>正是前一條路的下場。&lt;/p>
&lt;h2 id="相關閱讀">相關閱讀&lt;/h2>
&lt;ul>
&lt;li>概念地基：&lt;a href="https://tarrragon.github.io/blog/ddd/" data-link-title="DDD 領域驅動設計指南" data-link-desc="領域模型的理論與判準層：一袋欄位還是領域模型、什麼時候值得建 entity、不變式該落在哪一層強制、狀態轉換怎麼留下稽核軌跡、建構路徑怎麼設計。語言無關，實作限制路由到各語言模組。">DDD 領域驅動設計指南&lt;/a>——service 與 repository 的邊界、以及分工表裡「domain 持有的是能力需求」&lt;/li>
&lt;li>硬吞測試痛的反面教材：&lt;a href="https://tarrragon.github.io/blog/work-log/flutter_mock_infrastructure_overengineering_deleted/" data-link-title="1101 行自建測試基礎設施、重構刪掉 82.5% — 過度工程的三種形態" data-link-desc="自建測試基礎設施前先問框架的標準做法是什麼：mock 純資料物件、helper 帶並發鎖與記憶體洩漏防護、mock 放進 lib/ 進生產依賴圖，三種形態都在重新發明 Riverpod overrideWith 一行就有的東西。精緻的設計文件不是價值證明——它可以精心規劃一個不需要存在的系統。">1101 行自建測試基礎設施&lt;/a>——mock 難寫的兩種回應：修介面（本文）vs 蓋更大的 mock 系統（該篇）&lt;/li>
&lt;li>投機式預留的另一形態：&lt;a href="https://tarrragon.github.io/blog/work-log/flutter_async_query_overdesign_oscillation/" data-link-title="同一個子系統膨脹兩次：異步查詢系統的過度設計震盪" data-link-desc="過度設計會復發、且兩輪的機制不同：設計期的膨脹來自想像的需求（別層已處理的重試、用不到的優先級佇列），迭代期的膨脹來自不刪的舊版本（三個實作並存、狀態多處追蹤）。偽需求的檢驗法是問「這個能力已經有別層在做嗎」。">過度設計震盪&lt;/a>——那篇的迭代期沉積跟本文的預留方法同源，差別是 Port 案例的預留有 TODO 加啟用條件、不是無主地放著&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<blockquote>
<p><strong>觸發場景</strong>：Flutter 書籍管理 App 要幫 <code>SyncReadinessService</code> 寫測試，mock 設置一路撞牆：四個依賴共 55 個方法要配置、漏配就 MissingStubError、mockito 的嵌套 when 又有語法限制——而這個 service 實際呼叫的方法只有 5 個
<strong>疑問來源</strong>：測試這麼難寫，是測試工具的問題、還是被測物的問題？
<strong>整理目的</strong>：記下「mock 負擔」作為介面設計探針的判讀方式、以及 Port 介面（ISP）的落地步驟
<strong>本文邊界</strong>：素材是該專案 v0.18.6 的重構計畫（含依賴盤點數字與 wave 拆分）；Port 是 hexagonal architecture 的用語、這裡取其「消費端定義的窄介面」語意</p></blockquote>
<hr>
<h2 id="依賴盤點91-的-mock-配置是浪費">依賴盤點：91% 的 mock 配置是浪費</h2>
<p>寫不動測試的第一步是把依賴攤開來數。<code>SyncReadinessService</code> 的建構子收四個依賴，逐一盤點方法數與實際使用：</p>
<table>
  <thead>
      <tr>
          <th>依賴</th>
          <th>介面方法數</th>
          <th>實際呼叫</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>SyncRepository</code></td>
          <td>23</td>
          <td>2（待同步變更、待解衝突）</td>
      </tr>
      <tr>
          <td><code>BookRepository</code></td>
          <td>16</td>
          <td>1（getAllBooks）</td>
      </tr>
      <tr>
          <td><code>ChangeTracker</code></td>
          <td>10</td>
          <td><strong>0</strong></td>
      </tr>
      <tr>
          <td><code>EventBus</code></td>
          <td>6</td>
          <td>2</td>
      </tr>
  </tbody>
</table>
<p>合計 55 個方法、用 5 個。mock 這個 service 的依賴，等於為 50 個永遠不會被呼叫的方法做配置決策——每個都可能漏（MissingStubError）、每個都是測試檔的噪音。<code>ChangeTracker</code> 更直接：10 個方法、零使用，這個依賴純粹是建構子沿著前例複製來的。</p>
<p>數字本身就是診斷：<strong>mock 負擔正比於介面寬度、不正比於被測物的複雜度</strong>。測試難寫的根因不在 mockito、在被測物宣告的依賴遠寬於它需要的能力。</p>
<h2 id="往上追一個-repository五種職責">往上追：一個 Repository、五種職責</h2>
<p><code>SyncRepository</code> 的 23 個方法拆開看是五種職責：變更記錄管理（6）、同步任務管理（6、預留給 UC-08）、衝突解決（5）、離線佇列（4、預留 UC-09）、同步統計（2、預留 UC-10）。五分之三是<strong>為未來 use case 預留的投機式方法</strong>——現在沒有人呼叫、但每個消費者都被迫認識它們。</p>
<p>這是介面隔離原則（ISP）教科書式的違反現場，而它的第一個受害者是測試：生產程式碼呼叫方法時不在乎介面還有幾個方法、mock 卻要面對整個介面。<strong>測試是第一個被迫「完整消費」介面的客戶</strong>，所以介面過寬的痛總是先在測試爆。</p>
<h2 id="修法消費端定義的窄介面">修法：消費端定義的窄介面</h2>
<p>重構的核心動作是抽 Port——從消費者的實際需求出發定義介面、而不是從資料來源的能力出發：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dart" data-lang="dart"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">/// 從 SyncRepository 的 23 個方法中抽取實際需要的 2 個
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="kd">abstract</span> <span class="kd">class</span> <span class="nc">SyncQueryPort</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="n">List</span><span class="o">&lt;</span><span class="n">ChangeRecord</span><span class="o">&gt;&gt;</span> <span class="n">getPendingChanges</span><span class="p">({</span><span class="kt">int</span><span class="o">?</span> <span class="n">limit</span><span class="p">});</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="n">List</span><span class="o">&lt;</span><span class="n">ConflictResolution</span><span class="o">&gt;&gt;</span> <span class="n">getPendingConflicts</span><span class="p">({</span><span class="kt">int</span><span class="o">?</span> <span class="n">limit</span><span class="p">});</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1">/// 從 BookRepository 的 16 個方法中抽取實際需要的 1 個
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span><span class="kd">abstract</span> <span class="kd">class</span> <span class="nc">BookQueryPort</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="n">List</span><span class="o">&lt;</span><span class="n">Book</span><span class="o">&gt;&gt;</span> <span class="n">getAllBooks</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>三個配套讓改動保持小：</p>
<ul>
<li><strong>Repository 實作 Port、原介面不動</strong>：<code>abstract class SyncRepository implements SyncQueryPort</code>——既有實作自動滿足新介面、其他消費者不受影響</li>
<li><strong>Service 建構子改收 Port</strong>：依賴從 55 個方法縮到 5 個、<code>ChangeTracker</code> 直接移除；測試 mock 的對象變成 2 方法與 1 方法的小介面</li>
<li><strong>預留方法標記啟用時機</strong>：<code>// TODO(UC-08): 實際同步功能時啟用</code>——投機式方法不刪（設計已評估過）、但每個都有名字跟啟用條件，下次盤點時「這是預留還是死碼」有據可查</li>
</ul>
<p>值得注意方向性：Port 放在<strong>消費端的 domain 目錄</strong>（<code>synchronization/ports/</code>、<code>library/ports/</code>），因為它表達的是「這個 domain 需要什麼能力」、不是「Repository 提供什麼」。同一個 Repository 未來可以實作多個不同消費者的 Port，各自窄、互不牽連。</p>
<h2 id="判讀徵兆">判讀徵兆</h2>
<ul>
<li>mock 設置的行數超過測試本體——先數被測物依賴的介面方法數 vs 實際呼叫數</li>
<li>MissingStubError 反覆出現——每一次都是「介面要求你認識的方法」跟「你實際關心的方法」的差距</li>
<li>建構子的某個依賴在整個 class 內零呼叫——複製前例的沉積、直接刪</li>
<li>介面裡一半以上的方法標著「未來會用」——預留可以，但要有 TODO 加啟用條件，否則每個消費者與 mock 永遠陪葬</li>
</ul>
<p>「測試很難寫」在這個 case 裡是禮物：它比任何架構審查都早、都具體地量化了介面設計的問題（91% 這個數字就是證據）。把測試痛當成噪音硬吞（寫更肥的 mock helper）、跟把它當探針回頭修介面，是兩條分岔路——同專案的 <a href="/blog/work-log/flutter_mock_infrastructure_overengineering_deleted/" data-link-title="1101 行自建測試基礎設施、重構刪掉 82.5% — 過度工程的三種形態" data-link-desc="自建測試基礎設施前先問框架的標準做法是什麼：mock 純資料物件、helper 帶並發鎖與記憶體洩漏防護、mock 放進 lib/ 進生產依賴圖，三種形態都在重新發明 Riverpod overrideWith 一行就有的東西。精緻的設計文件不是價值證明——它可以精心規劃一個不需要存在的系統。">mock 基礎設施過度工程</a>正是前一條路的下場。</p>
<h2 id="相關閱讀">相關閱讀</h2>
<ul>
<li>概念地基：<a href="/blog/ddd/" data-link-title="DDD 領域驅動設計指南" data-link-desc="領域模型的理論與判準層：一袋欄位還是領域模型、什麼時候值得建 entity、不變式該落在哪一層強制、狀態轉換怎麼留下稽核軌跡、建構路徑怎麼設計。語言無關，實作限制路由到各語言模組。">DDD 領域驅動設計指南</a>——service 與 repository 的邊界、以及分工表裡「domain 持有的是能力需求」</li>
<li>硬吞測試痛的反面教材：<a href="/blog/work-log/flutter_mock_infrastructure_overengineering_deleted/" data-link-title="1101 行自建測試基礎設施、重構刪掉 82.5% — 過度工程的三種形態" data-link-desc="自建測試基礎設施前先問框架的標準做法是什麼：mock 純資料物件、helper 帶並發鎖與記憶體洩漏防護、mock 放進 lib/ 進生產依賴圖，三種形態都在重新發明 Riverpod overrideWith 一行就有的東西。精緻的設計文件不是價值證明——它可以精心規劃一個不需要存在的系統。">1101 行自建測試基礎設施</a>——mock 難寫的兩種回應：修介面（本文）vs 蓋更大的 mock 系統（該篇）</li>
<li>投機式預留的另一形態：<a href="/blog/work-log/flutter_async_query_overdesign_oscillation/" data-link-title="同一個子系統膨脹兩次：異步查詢系統的過度設計震盪" data-link-desc="過度設計會復發、且兩輪的機制不同：設計期的膨脹來自想像的需求（別層已處理的重試、用不到的優先級佇列），迭代期的膨脹來自不刪的舊版本（三個實作並存、狀態多處追蹤）。偽需求的檢驗法是問「這個能力已經有別層在做嗎」。">過度設計震盪</a>——那篇的迭代期沉積跟本文的預留方法同源，差別是 Port 案例的預留有 TODO 加啟用條件、不是無主地放著</li>
</ul>
]]></content:encoded></item></channel></rss>