---
title: "幫一個複雜的 component 寫測試，該測到多細？"
description: "探討 component testing 的責任劃分和測試範圍"
lang: "zh-Hant-TW"
tags: ["RTL", "testing", "component-testing", "test-coverage"]
pubDate: 2022-10-25T00:00:00.000Z
---

和同事討論一個複雜的複合型 component 的測試原則，以及不同層級間 components 責任的劃分，以下是一些粗略想法的筆記。

## RTL 作者對元件測試的建議

- spec 的定義方式，應該用接近使用者實際使用預期的方式去定義
- 測試應該用接近使用者行為的方式去進行測試

## 測試的目標

- 測試是為了保護模組的行為符合 spec，也就是說，要確保測試的對象，能遵照其責任，完成任務
  - 實作上，spec 就是寫測試時我們去 assert 的內容。也就是說，我們其實會把 spec 寫在測試的程式碼中。
    - 由此延伸一個點，反過來說，實際上測試裡的 spec 有沒有涵蓋到「真正的 spec」，是要看 assert 的內容。coverage 只是在測試中有沒有跑過這段程式碼。高涵蓋率為「測試完備」的必要而非充分條件。
      - 「測試涵蓋率是測試完備的必要非充分條件」這個問題，在所有類型的測試都會有，但因為寫比較大的 components 的測試時，一次 render 裡面涵蓋的 spec 量較多（要 assert 的東西比較多），同樣滿足涵蓋率的條件下，比較有可能會有沒 assert 到的 spec。
        - 目前沒有想到有什麼自動化的解法，只能人工確認重要的 test cases 是否都有進行測試和 assert。
        - 因為要人工檢查哪些東西該 assert ，最好有個 check list 參考

## 不同層級元件的測試劃分

- React component 是一層 render 一層，上層 component 的測試，有需要測到子 component 的行為嗎？

  - 釐清 component 的責任範圍，同時也就釐清該測試的範圍，同時也是釐清我們把 spec 寫在哪裡
  - 要回答上述問題，就要釐清 parent component 的責任：
    - parent component 的責任是什麼？
      - render 出這個 component 該 render 的東西：也就是處理 input，再以特定的方式呼叫 children component，並組合其結果。
    - children component 的責任，會被包含在 parent component 中
      - 例如：一張有很多 input 的表單，它的責任有在錯誤狀態時在錯誤的 input 顯示錯誤訊息；單一 input component 的責任有在自己錯誤狀態時顯示錯誤訊息。

- parent component 的責任和 children component 的責任，必然會有重疊的部分，我們應當把重疊的部分交給 children component 的測試，去幫我們保證 parent component 責任的履行嗎？
  - 理論上不一定要把所有 spec 的檢查都在最上層做。但必須確保每一個 spec 在程式中都有地方 assert 到（但可能沒有自動化的方法可以檢查）。（例如上述 input 錯誤訊息內容的特定規格，或許可以只在 input 的測試檢查）
  - 怎麼做？
    - 目前根據前述 RTL 的建議，是選擇第二種測法，也就是讓 interface 變動彈性成本較低
    - 第一種測法：在 parent 測各種情況下 children component 是否被正確的呼叫、給予參數，在 children 測各種 inputs 是否 render 對的結果
      - 好處：
        - parent 不用管 children 的 spec 為何。children spec 修改就由 children 的測試負責。
      - 代價：
        - spec 沒有修改但 children 介面修改時，會要改 parent 的測試。改動 children 介面成本增加。
    - 第二種測法：在 parent 測各種情況下 parent 回傳的結果，側重於測是否被正確的組合成 parent 的回傳結果
      - 好處：
        - parent 測試不需要考慮 children 的介面，只在意 parent 的結果
      - 代價：
        - parent 的測試範圍也會包括到部分 children 的 spec，會在 parent 的測試和 children 的測試重疊到一些 spec。
          - 重疊的 spec 如果有修改就要同時改兩邊的測試，所以在寫 parent 測試的時候會思考怎麼不涉及 children spec 的情況下能 assert parent 的責任範圍
  - 什麼東西寫在 children 測，什麼東西寫在 parent 測，要考慮：
    - children 介面的穩定度？children 介面是否容易被拆解或替換？
      - 穩定度高的 children 才適合把測試分給它
    - 模組的規模？
      - 當一個模組要負責的 spec 內容太多時，可考慮這個模組的責任是不是太多，是否應該拆分模組或委交下層模組測試
  - 其實不只 React component，所有在 function 中執行 function 的程式，都可以用同樣方式思考其測試
