UNPKG

deepmerge-plus

Version:

用於深度(遞迴)合併 JavaScript 物件的函式庫 / A library for deep (recursive) merging of JavaScript objects

292 lines (224 loc) 7.59 kB
# 快照測試慣例 / Snapshot Testing Convention --- ## 0. 與傳統斷言的比較 | 面向 | 傳統斷言 | 文件化快照 | |------|---------|-----------| | 主要目標 | 驗證正確性 | 行為展示 + 驗證 | | 可讀性 | 取決於測試名稱 | 快照本身即文件 | | 維護成本 | | 中(需更新說明) | | 適用場景 | 單元測試 | API 範例、教學 | --- ## 1. 快照物件結構 ```typescript expect({ explain: '...', // 測試說明(必填) tags?: 'tag1, tag2, ...', // 分類標籤(可選,有特別意義或需檢索時使用) result: { ... }, // 輸出結果(必填) input?: { // 輸入資料(可選,簡單時保留) target: { ... }, source: { ... }, }, }).toMatchSnapshot() ``` ### 必填欄位 | 欄位 | 類型 | 說明 | |------|------|------| | `explain` | `string` | 測試說明(必填,作為標題/描述/邏輯解釋,單行或多行皆可) | | `result` | `object` | 函式輸出結果(必填,始終保留) | ### 可選欄位 | 欄位 | 類型 | 說明 | |------|------|------| | `tags` | `string` | 分類標籤(以 `, ` 分隔),只有當測試項目有特別意義或需要被檢索時才需要加上 | | `input` | `object` | 輸入資料(來自函式參數,簡單時保留) | --- ## 2. 欄位規範 ### 2.1 explain - 測試說明 - **必填**:每個快照至少需要 `explain` - **用途**:幫助快速理解快照的內容與邏輯 - **類型**: - **標題式**:簡短說明測試目的 - **描述式**:描述預期行為 - **邏輯式**:解釋合併邏輯 - **複合式**:結合以上,以快速理解為主 - **格式**:雙語(繁中 + 英文)或純英文 - **長度**:可以是**單行**或**多行**文字,視說明需求而定 - **擴展用途**:可以補充說明 `result` 中各欄位的來源或意義,幫助理解輸出結構 ```typescript // 短句範例 explain: '✅ 新增鍵 / Add keys' // 複雜範例:說明 result 中各欄位的來源 explain: '✅ 多個物件合併(不同類型):所有屬性被合併 result 中 - first: 來自第一個物件 - second: 來自第二個物件 - third: 來自第三個物件 - fourth: 來自第四個物件' ``` ### 2.2 tags - 分類標籤 - **格式**:以 `, ` (逗號 + 空格)分隔的多重標籤 - **語法**:字串形式 `tag1, tag2, tag3`(非陣列) - **用途**:人類可讀的分類識別 - **使用時機**:只有當測試項目有特別意義或需要被檢索時,才需要加上 `tags` ```typescript // 正確 tags: 'basic, root-level, keys' // 錯誤(陣列形式) tags: ['basic', 'root-level'] ``` ### 2.3 input - 輸入資料 - **來源**:來自函式參數,不一定是 `target` / `source` - **結構**:根據函式簽章決定(如 `{ array: [...] }`、`{ options: {} }`) - **判定邏輯**: ``` input 複雜度判定 屬性數量 3 無深度嵌套? ├─ 保留 input └─ 移除 input(依賴 result 推導) ``` ```typescript // 簡單案例:保留 input(來自函式參數) input: { array: [1, 2, 3] } input: { target: { a: 1 }, source: { b: 2 } } // 複雜案例:移除 input // (依賴快照差異顯示輸入輸出變化) ``` ### 2.4 result - 輸出結果 - **必填**:始終保留 - **用途**:斷言驗證 + 行為展示 --- ## 3. tags 命名慣例 ### 3.1 常用標籤分類 | 類別 | 標籤範例 | 說明 | |------|---------|------| | **測試類型** | `basic`, `advanced`, `edge-case` | 測試複雜度 | | **資料結構** | `object`, `array`, `nested`, `deep` | 輸入資料類型 | | **功能特性** | `clone`, `merge`, `replace` | 合併行為 | | **應用場景** | `config`, `preferences`, `state` | 實際應用 | | **對照組** | `good-example`, `bad-example` | 展示正確/錯誤用法 | ### 3.2 命名格式 - 使用 **小寫字母** - 使用 `-` `_` 分隔詞彙:`nested-object`, `deep_merge` - 避免中文標籤 ```typescript // 正確 tags: 'basic, nested-object, merge' // 錯誤 tags: '基礎, 巢狀, 合併' ``` --- ## 4. 範例 ### 4.1 簡單案例 ```typescript it('should add keys to empty target', () => { const target = {}; const source = { key1: 'value1', key2: 'value2' }; const result = merge(target, source); expect({ explain: '✅ 新增至空物件 / Add to empty object', tags: 'basic, root-level, keys', input: { target, source }, // 來自函式參數 result, }).toMatchSnapshot(); }); ``` ### 4.2 deepmergeAll 案例 ```typescript it('should merge array of objects', () => { const input = [{ a: 1 }, { b: 2 }]; const result = deepmergeAll(input); expect({ explain: '合併物件陣列', input: { input }, // 參數名稱為 input result, }).toMatchSnapshot(); }); ``` ### 4.2 複雜案例 ```typescript it('should deeply merge nested objects', () => { const target = { user: { name: 'Alice', settings: { theme: 'dark' } } }; const source = { user: { age: 30, settings: { language: 'en' } } }; const result = merge(target, source); expect({ explain: 'Deep nested merge preserves nested structure', tags: 'nested, deep, object-merge', result, }).toMatchSnapshot(); }); ``` ### 4.3 比較測試(對照組) 當需要比較 deepmerge 與其他函式庫(如 lodash)的行為差異時使用。 ```typescript it('should match lodash _.assign behavior', () => { const target = { a: 1, b: 2 }; const source = { b: 3, c: 4 }; // deepmerge 結果 const resultDeepmerge = deepmerge(target, source); // lodash 結果 const resultLodash = _.assign({}, target, source); expect({ explain: `deepmerge 模擬 _.assign: - target: { a: 1, b: 2 } - source: { b: 3, c: 4 } - 結果:b source 覆蓋`, result: { resultDeepmerge, resultLodash, isEqual: resultDeepmerge.a === resultLodash.a && resultDeepmerge.b === resultLodash.b && resultDeepmerge.c === resultLodash.c }, }).toMatchSnapshot(); }); ``` **快照結構**: ```json { "explain": "deepmerge 模擬 _.assign:...", "result": { "resultDeepmerge": { "a": 1, "b": 3, "c": 4 }, "resultLodash": { "a": 1, "b": 3, "c": 4 }, "isEqual": true } } ``` **說明**: - 使用 `resultDeepmerge` `resultLodash` 兩個鍵名(而非 `deepmergeResult` / `lodashResult`) - 最後一個鍵為 `isEqual` 用於快速識別結果是否相同 - explain 使用模板字串(反引號)進行多行說明 --- ## 5. 相關資源 - [Jest Snapshot Testing](https://jestjs.io/docs/snapshot-testing) - [deepmerge 測試檔案](./test/) --- ## 6. 核心規範速查 ### 最小結構 ```typescript expect({ explain: '說明 / 描述 / 邏輯解釋', // 必填(標題/描述/邏輯/複合式) result: { /* ... */ }, // 必填(始終保留) }).toMatchSnapshot() ``` ### 完整結構 ```typescript expect({ explain: '說明 / Description', // 必填 tags?: 'tag1, tag2, tag3', // 可選 result: { /* ... */ }, // 必填 input?: { target, source }, // 可選 }).toMatchSnapshot() ```