﻿# 一次搞懂單元測試、整合測試、端對端測試之間的差異

網站建置不是件簡單的事，我們都知道網站做好之後，有好多細節需要兼顧，所以許多公司花了大量的時間與金錢，耗用人力對維護中的網站進行不斷的、重複的人工測試，想達到的目的不外乎是希望網站不要出錯，可以給客戶/使用者最好的網站使用體驗。但你是否知道**單元測試**、**整合測試**、**端對端測試**之間的差異在哪裡？本篇文章將闡述他們之間不同的部分。

我這幾年經常擔任企業技術顧問，蠻多人向我提出「自動化測試」的可行性，我基本上強烈建議與鼓勵客戶導入測試，無論是 [單元測試](https://zh.wikipedia.org/wiki/%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95) ([Unit testing](https://en.wikipedia.org/wiki/Unit_testing))、[整合測試](https://zh.wikipedia.org/wiki/%E9%9B%86%E6%88%90%E6%B5%8B%E8%AF%95) ([Integration testing](https://en.wikipedia.org/wiki/Integration_testing))、端對端測試 ([E2E testing](https://www.techopedia.com/definition/7035/end-to-end-test))，通通都好。重要的是能開始改變觀念，漸漸透過自動化測試取代人工測試，為的不單單只是降低成本，而是自動化測試帶來的優勢無可取代，速度快、品質高、可重複、彈性大、自動化，重要性不言可喻！

### 測試的種類

在不同的測試類型中，所需要保護的面向不太相同，以下是不同測試類型的大致介紹：

-   **單元測試 (Unit testing)**
    
    以程式碼的最小單位進行測試，保護程式邏輯不會在系統維護的過程中遭到破壞，也進一步確保維護中的程式碼品質。
    
    這種測試類型通常由開發人員自行撰寫，自己寫的 Code 自己寫測試，有經驗的開發人員可以用非常有效率的方式撰寫單元測試，因為測試範圍小，這種類型的測試通常不需要設立測試環境，因此可以得到較高的撰寫效率，也是所有測試類型中最容易撰寫的測試類型。不過，對於一個沒有經驗的開發者來說，撰寫單元測試可能會耗用大量時間，寫測試程式的時間很有可能會遠大於實際撰寫程式碼的時間，有蠻多人會因為這樣而放棄撰寫單元測試。
    
-   **整合測試 (Integration testing)**
    
    整合多方資源進行測試，確保模組與模組之間的互動行為正確無誤，也讓不同模組在各自開發維護的過程中不會因為功能調整而遭到破壞。
    
    這種類型的測試通常介於單元測試與端對端測試之間，有時候會由專職的測試人員進行開發，但大部分還是由開發團隊中負責特定模組的人來撰寫。有時候單一模組即便完全通過單元測試，獨立運作也正常，但是當需要與其他模組互動時，也是有可能發生錯誤，這時就是整合測試的主要負責領域。
    
    以下是未通過整合測試的案例：(兩個元件在整合時發現問題)
    
    ![image](https://stwillblogassets.blob.core.windows.net/files/images/external/user-images.githubusercontent.com/3ccf7fc3d9c0766629a0-52933895-c0d47600-338f-11e9-9034-11e1ad0c42f1.gif)
    
-   **端對端測試 (End-to-end testing) (E2E testing)**
    
    所謂的「端對端」(E2E) 是指從使用者的角度出發(一端)，對真實系統(另一端)進行測試。
    
    這種類型的測試對許多公司來說，就是「人工測試」的主要範圍，因為你可以透過人工對已經完整部屬的網站進行測試，因此可以驗證出系統是否符合客戶的實際需求。這部分也可以透過撰寫 E2E 測試程式來進行自動化，增加測試效率。
    
    這裡有個[未通過 E2E 測試的案例](https://twitter.com/i/status/1078363114666627072)相當有趣，雖然每個整套系統每個模組都通過所有單元測試與整合測試，但最後組裝起來後，從使用者的角度無法接受！
    

你可以看到，在大部分的公司裡，**單元測試**與**整合測試**固然重要，但卻最少人投入，一來學習測試開發有點門檻，需要花上一些時間熟悉與試錯；二來維護測試的過程，通常會隨著需求變更而導致成本大幅增加；第三，**單元測試**與**整合測試**都無法確保程式最終的執行結果是客戶真正要的，因為這兩種測試類型，一個是針對程式碼的最小單位進行測試，另一個是針對模組與模組之間的整合進行測試，就算測試結果是成功的，最終還是無法確保**需求正確**這件事。

**端對端測試**則完全不同，它以真實、完整的系統進行測試，從開啟瀏覽器開始，一步步的操作與輸入，並且讓瀏覽器與後端 API 進行互動，然後直接透過最終的顯示狀態來診斷其結果是否符合預期。如果測試結果正確，通常也意味著「需求正確」，因此這可以說是最容易讓客戶、老闆放心的測試類型。也是為什麼一般公司寧願不去寫單元測試、整合測試，卻願意投入端對端測試的原因。很可惜的是，大部分的端對端測試都是**人工進行**的！

### 撰寫測試程式的價值

其實並不是所有網站都**值得**撰寫測試，對於一個**需求不確定**、**價值性不高**的網站來說，撰寫測試就是浪費時間，任何類型的測試都一樣。

-   **需求不確定**
    
    有時候專案建置初期，在**需求尚未明朗又必須交付成果**的時候，撰寫測試就顯得不具意義，因為當需求改變時，測試程式肯定也要跟著改寫，開發成本也會跟著墊高。
    
    業界有很多人推廣 TDD (測試先行開發)，工程師可藉由撰寫測試程式碼來進一步釐清設計需求，從輔助設計的角度出發，的確是不錯的點子。但是這樣的點子不見得適用於所有專案類型，像是 PoC (驗證可行性) 的專案，對於設計架構上就沒有特別要求，這時後撰寫測試就會增加開發時間，反而降低業務競爭力 (因為拉長提案的時間)。
    
-   **價值性不高**
    
    網站的價值性，取決於網站經營者對價值的設定，其實跟工程師沒多大關係。我們都知道撰寫測試需時間，而時間就是金錢，當客戶、老闆不願意給予合理的工時撰寫測試，自然也就沒有撰寫測試的可能性。如果客戶不給足夠的人力資源，用戶不給足夠的開發時間，同時又要求高品質的軟體產出，你應該很清楚接下來該怎麼做了！
    
    市面上講授測試的書籍與課程都不少，但企業界實際導入測試開發的卻依然是寥寥無幾，你可以說大家所做的網站都不重要嗎？No! 那是因為價值的定位並不是你說重要就重要，最終還是依賴上位者如何感受撰寫測試的價值，當老闆感受不出價值，自然沒有投入自動化測試的必要。如果老闆知道叫個人把網站功能全部使用一遍 (端對端測試)，就能知道網站是否可用，那我想他會毫不遲疑的請個人去測一遍網站所有功能。**因為這是他唯一能理解的價值呈現方式**！
    

這三種不同類型的測試，其實也有不同的價值定位：

![image](https://stwillblogassets.blob.core.windows.net/files/images/external/user-images.githubusercontent.com/61356eb63577ce1d5888-52933856-9f738a00-338f-11e9-9ca0-24431d6a7b3e.png)

-   **單元測試 (Unit testing)**
    
    單元測試以程式碼的最小單位進行測試，所以這種測試類型通常執行速度快 (測試範圍小)、可靠度高 (確保小部分程式碼正常運作)、有效隔絕失敗 (從數萬行程式碼之間隔絕出可能的錯誤)。
    
    不過實務上單元測試相當考驗系統架構的能力，程式架構不夠 [S.O.L.I.D.](https://www.udemy.com/solid-csharp/?couponCode=WILL.FANS) 通常也意味著單元測試很難寫。當單元測試很好寫的時候，通常也代表著你的程式架構還算漂亮。
    
    照理來說，單元測試的比例應該是所有測試中最高的，因為開發的成本相對低廉。(但前提是架構漂亮的程式，單元測試才會好寫)
    
-   **整合測試 (Integration testing)**
    
    整合測試會跨越單元測試的範圍，對不同模組之間的交互作用進行測試，意味著你也要準備更完整的模擬環境，來幫助你完成測試工作。
    
    一般來說，整合測試的數量應該介於單元測試與端對端測試之間，針對幾個主要的模組進行整合測試即可。
    
-   **端對端測試 (End-to-end testing) (E2E testing)**
    
    端對端測試是從使用者的角度出發對系統進行測試，你所要準備的測試環境就是一套完整的系統部署，如果能透過 Docker 容器技術進行部署，那麼設立測試環境的成本將會大幅降低。
    
    由於端對端測試可以正確反映出使用者需求是否滿足，因此商業價值較高，但要對一個複雜的系統進行完整的 E2E 測試開發，可能會有相當多的測試案例，如果真的要做到 100% 的測試覆蓋率，成本也會相對的提高許多！
    

### 自動化端對端測試的價值

端對端帶來極高的商業價值，就因為端對端測試的結果也代表著你從使用者的角度驗證需求成功，所以即便**人工測試**的成本極高，大家還是會很自然的主動地投入。但是人工的端對端測試，帶來了許多問題，你可以看看以下例子，你中了幾箭？

-   工程師在改 Code 的過程中，A 功能改好了，但 B 功能卻壞掉了
-   網站請工程師修正一個**小問題**的時候，程式沒出錯但畫面卻跑版了
-   原本勾選一個核取項目(Checkbox)後應該出現某個欄位，但某次更新後欄位卻不再顯示，但人工測試時沒發現，是客戶主動通知才知道有這個問題
-   客戶上個月提出的修正雖然已經改好，但最近的版本卻又不小心改壞了
-   工程師因為被老闆/客戶/使用者罵過太多次，每次改完程式都**很害怕**不知道會不會改壞其他功能

人非聖賢、孰能無過，人工測試總有疏漏之處，就算你有完整的測試案例與腳本，測試人員也不見得會 100% 照著 SOP 進行操作，畢竟重複的工作做久了都會厭煩，打從心裡認為不會出錯的功能，自然也會測試的比較隨便些，這是人之常情。

因此，導入自動化的端對端測試，絕對有其必要性，尤其像是針對企業 LOB 應用 (如 EIP、CRM、ERP 等等)、價值型高的網站 (如電子商務、品牌形象網站)、操作動線複雜的企業表單、網站內的重要功能 (註冊會員、登入、購物車、結帳)...等等，都可以認真考慮開始慢慢加入自動化的端對端測試，用程式來跑測試，原本跑一輪測試要花上 20 分鐘，寫完測試程式後，可能只需要 20 秒就可以完成，對一個**穩定的產品**或**需要長時間維護的專案**來說，自動化的端對端測試絕對值得投資，最終所節省的成本也絕對遠大於人工測試的成本。

再舉一個實際的例子，由 Google 所發展的 Angular Material 專案是一個 UI 元件庫，該元件庫有個很重要的目的，就是讓各種不同的上網裝置都能有一致的瀏覽體驗，對於 UI 上的要求也非常細緻，這樣的元件庫能怎樣撰寫測試？是的！他非常需要完整的 E2E 測試，因為唯有在瀏覽器上呈現正確的結果，才能真正符合用戶的需求！因此 Angular Material 的 E2E 測試專案中擁有上千個螢幕截圖的測試案例，每次有新的版本 commit 就會全自動的執行每個元件在不同瀏覽器下的執行結果，並且透過自動化的瀏覽器畫面截圖，比對是否因為版本的變更而導致 UI 發生任何 1px 的誤差，如此一來也才能真正確保每次釋出新版本不會有任何問題。關於這個案例，你可以看看 [Protractor: A New Hope - Michael Giambalvo, Craig Nishina](https://www.youtube.com/watch?v=6aPfHrSl0Qk&feature=youtu.be&t=380) 這場演講的內容。

### 我的網站值得我花時間撰寫自動化的端對端測試程式嗎？

所以你在開始撰寫端對端測試之前，應該還是要先想想：

-   你的網站是否有**重要的**功能需要被保護
-   你的網站是否有**穩定的**需求需要被保護

我們以電子商務為例，照理說將商品放入購物車並成功結帳是最重要的操作，如果有人修改了一個功能，導致購物車在結帳時會卡關，發現問題的時候已經過了 2 個小時，如果你是老闆/客戶你會怎麼想？像這種超級超級重要的功能，就應該優先撰寫自動化的端對端測試，讓每一個程式版本要上線前，都全自動的跑過一遍端對端測試，確保所有**重要的**功能可以正確無誤的被執行。

我們以品牌官網為例，如果因為你調整了一個很簡單的 CSS 樣式，導致網站出現了一條詭異的白線 (輕微破版)，這條白線可能會自動被工程師給忽略，因為工程師實在沒啥美感，對於一兩條白線可能不以為意，新版網站上線幾天後才被發現有這個問題，對品牌造成的傷害又多少，那也就很難說了。

我們再以企業表單為例，複雜的表單操作通常意味著相當複雜的操作邏輯，而這個邏輯通常被綁在特定人身上，當人事異動與工作調轉發生時，這複雜的商業邏輯與操作行為，可能就會從此遺失，接手的人在缺乏文件輔助的情況下，可能會用非常奇怪的操作方式使用網站，相對地出問題的機會也就增加。這時自動化的端對端測試就可以當成反向工程的材料，從程式碼來反推當時的確切需求！

### 我的自動化端對端測試程式應該覆蓋到我多少重點需求？

有時候一個看似簡單的企業表單，也許網站頁面就一兩頁而已，但需求說明文件可能會超過 50 頁這麼多，我們就承接過這樣的表單專案，真的相當複雜。

所以一個企業表單就可能有數十種到上百種操作方式，有時候將所有可能的操作動線都寫成自動化的端對端測試程式腳本是不切實際的。除非你真的認為每一個步驟的重要性真的都很高，客戶不容許有任何一點失誤，那就真的有可能需要投入 100% 的測試覆蓋率 (確保程式運作與實際需求正確無誤)。但實務上來說，我們都會優先針對**商業價值最高的功能**進行端對端測試開發，用以保護該網站最重要的需求不會在網站維護的過程中降低服務品質！

### 如何導入自動化的端對端測試

要開始學習自動化的端對端測試，可以參考以下幾個步驟：

1.  **選擇一個好用的測試框架**
    
    基本上可以模擬人類對網站進行測試有很多方法，你用任何一種程式語言都可以實現自動化的端對端測試。不過一般來說我們還是會選擇一套不錯的框架，以降低初期的學習成本。
    
    市面上現有的 E2E 框架非常多，知名的有 [Protractor](http://www.protractortest.org/)、[CasperJS](http://casperjs.org/)、[Nightwatch.js](http://nightwatchjs.org/)、[Testcafe](https://devexpress.github.io/testcafe/)、[Cypress](https://www.cypress.io/) 等等。
    
    我們這些年來嘗試過很多套 E2E 框架，但最終還是以 [Protractor](http://www.protractortest.org/) 為主，最主要的一個理由就是：這套 E2E 框架對 Angular 的支援度最高，有蠻多專門針對 Angular 所設計的 API 可用，對於正在使用 Angular 前端框架的開發者來說，採用 Protractor 可以在短時間內入手，學習門檻相對較低。不過 Protractor 並不限於用在 Angular 前端框架，就算你用 VueJS 或 React，甚至用 jQuery 的傳統前端開發者，一樣也可以透過 Protractor 實現完整的 E2E 自動化測試。
    
2.  **挑選網站中最重要且需要保護的功能**
    
    我們知道實現 E2E 自動化測試的成本並不低，也知道實現 100% 的 E2E 測試是不太務實的做法，因此選擇相對重要的功能進行 E2E 測試開發，是相當重要的一個步驟，你必須設定好目標，逐步來實現 E2E 開發。
    
    另外一個挑選的法則就是，當你的網站在人工測試的過程中，發現一個錯誤就加入一個相對應的 E2E 測試，這樣至少可以保證日後不會再出現相同問題，減少之後[回歸測試](https://zh.wikipedia.org/wiki/%E5%9B%9E%E5%BD%92%E6%B5%8B%E8%AF%95)的時間，如此一來也可以大幅提升程式品質。
    
3.  **整合 CI/CD 持續建置/持續交付**
    
    自動化測試如果可以整合到 CI/CD 流程中，那是最完美的了。每一次的新版本如果都能自動化的進行 E2E 測試，更能確保網站在上線之前不會發生重大錯誤。如果測試的覆蓋率夠高，並且可以放心 E2E 測試的品質，甚至可以做到全自動上版！
    
    透過 CI/CD 進行自動化的執行 E2E 測試還有另一個好處，那就是開發人員不用自己執行 E2E 測試，所以不會因為執行測試而中斷開發作業，所有測試都在 CI 伺服器執行，只要發現到測試失敗就會自動寄信通知開發者，當有問題發生的時候，也可以在第一時間得知與修正。
    

萬事起頭難，現在最重要的就是踏出第一步，開始撰寫測試！
