# 我的軟體測試概論 ###### tags: `QA` `Plus` `Testing` 👣參考書目 === 1. The Art of Software Testing 3rd Edition by Glenford J. Myers (1979) 2. Lessons Learned in Software Testing by Cem Kaner, James Bach, Bret Pettichord (2001) 3. The Dangers of Use Cases Employed as Test Cases by Bernie Berger (2001) 4. Software Testing - Carnegie Mellon University Dependable Embedded Systems by Jiantao Pan (1999) 📕名詞說明 === ### • **軟體測試心理學(The Psychology of Testing)** > [Glenford J. Myers[1]](#👣參考書目)所提出對於軟體測試常見謬誤:*(1)測試是證明產品不存在錯誤的過程 (2)測試是建立信心的過程,確保一個程式能夠正確執行它所預期的功能* ; 軟體測試應作為提升產品品質的一種手段。 ### • **軟體測試經濟學(The Economics of Testing)** > [Glenford J. Myers[1]](#👣參考書目)所提出軟體測試應著重執行過程的經濟性與效益,例如:*執行測試所費時間是否大於開發需時間?*,因此必須針對測試制定的相關策略(strategies)。 ### • **測試案例(Test case)** > 分為可形式化(Formal test cases)和非形式化(Informal test cases)測試案例。依據[artoftesting.com](https://artoftesting.com/test-case)定義參考 ### • **測試準則(Test oracle)** > [William E. Howden](https://en.wikipedia.org/wiki/Test_oracle#cite_note-2)於1978年所提出測試準則(Test Oracle)就是執行某測試案例對預期結果的判斷與解讀。 ### • **測試覆蓋率(Test coverage)** > 測試覆蓋率是在軟體測試或是軟體工程中的軟體度量,表示軟體程式中被測試到的比例。依測量方式有許多不同種類的測試覆蓋率: > > 1. 代碼覆蓋率(code coverage) > 2. 特徵覆蓋率(feature coverage) > 3. 情景覆蓋率(scenario coverage) > 4. 屏幕項目覆蓋率(screen item coverage) > 5. 模組覆蓋率(model coverage) 📃測試方法分類(Test approaches) === ## Ⅰ.🔮可形式化測試案例(Formalized) ### ⒈開發參與度測試分類:黑箱/白箱/灰箱 > • **開發參與度測試分類簡介:** > > ㅤㅤ*黑箱測試(Black Box Testing)/白箱測試(White Box Testing)/灰箱測試(Gray Box Testing)*,是依據測試人員對開發參與度測試所做分類,黑箱測試又名[*input/output driven testing*](#%F0%9F%91%A3%E5%8F%83%E8%80%83%E6%9B%B8%E7%9B%AE),主要關注於測試軟件的輸入與輸出,並不了解軟體內部如何設計與撰寫;白箱測試又稱[*glass box testing*](#%F0%9F%91%A3%E5%8F%83%E8%80%83%E6%9B%B8%E7%9B%AE),與黑箱相反是透過對軟體內部設計與撰寫內容拆解後進行測試;灰箱測試則是介於黑盒與白盒之間,對軟體內部的設計與架構有了解情況後進行測試。 > > • **測試方法比較:** > > > ㅤ | **黑箱測試** | **白箱測試** | **灰箱測試** > > -- | -- | -- | -- > > **開發參與程度** | 低(Tester) | 高(Developer) | 中等(SDET) > > **測試涵蓋率** | 通常低 | 通常高 | 普通 > > **測試案例複雜度** | 通常低 | 通常高 | 普通 > > **測試經濟學** | 經濟性高 | 經濟性低 | 經濟性低 > > **測試心理學** | 易尋心理盲區 | 難尋心理盲區 | 普通 > > • **什麼時候最適合黑箱測試?** > > > ⒈添加新功能或規格異動 > > ``` > > 今有一個購物網站系統增加回饋金促銷活動 > > > > 測試動作: > > 1. 消費者購買皮鞋贈送100元回饋金 > > 2. 消費者使用該100元回饋金購買運動鞋, > > 3. 消費者將皮鞋退貨 > > > > 預期結果: > > 退貨後將皮鞋價格扣除贈品後退還給消費者 > > > > 測試結果: > > 消費者再將皮鞋退貨後,系統僅將皮鞋原價退款。最終消費者無償獲得100元回饋金。 > > ``` > > > ㅤ由上敘描述中可以發現,軟體專案開發添加一個新功能,往往需要修改多個相關聯模組,因開發者都專注在特定範圍的設計邏輯當中,而產品設計者無法將狀況與結果窮舉出來,依照上描述 : 「退貨返還皮鞋售價看似有合理」,但測試結果顯示這是退貨規則漏洞: **消費者可巧取方式無嘗代價獲得回饋金**,購物網站正確流程應為:「退貨應返還皮鞋售價並再扣除100元回饋金」。==黑箱測試易站在終端使用者的角度,較易發現[測試心理學](#%E2%80%A2-%E8%BB%9F%E9%AB%94%E6%B8%AC%E8%A9%A6%E5%BF%83%E7%90%86%E5%AD%B8The-Psychology-of-Testing))當中的盲區。== > > > > > ⒉ 具備圖形介面(GUI) > > > ![](https://hackmd.io/_uploads/B1ZIgP5Bn.gif) > > > > > > ㅤGUI圖形輸入介面按鈕因時序其輸入方式的排列組合趨近無限多種,且軟體設計初期難以預估使用者的硬體配備,因此*圖形輸入介面是無法將所有狀況都測試過一次*,以上描述例子來說該按鈕啟用功能於結束前應不能重複點擊,由於開發者未遵循[單例模式](https://zh.wikipedia.org/zh-tw/%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F)原則,造成短時間內連續點擊該按鈕後造成錯誤,透過對設備端的測試可得出連點之間相隔時間差,可做為開發人員修復錯誤的依據。==黑箱測試可在模擬使用者環境下進行測試,易透過操作輸入介面方式找出問題點==。 > > > > • **什麼時候最適合白箱測試?** > > > > ⒈ 難保證測試覆蓋率 > > ```md > > 今有一個結算系統能將所有帳務支出/收入作加總 > > > > 測試動作: > > 1. 此帳務有兩筆收入 : +0.1 , +0.2 > > 2. 點選結算按鈕後觀察加總結果 > > > > 預期結果: > > 帳務共兩筆收入0.1+0.2加總結果應顯示0.3 > > > > 測試結果: > > 帳務共兩筆收入0.1+0.2加總結果顯示0.30000000000000004 > > ``` > > > ㅤ上述例子是程式語言常見的[浮點數運算問題](https://0.30000000000000004.com/),加總是幾乎都會用到的運算方式,設計上雖然簡單但如果施作黑箱測試,會發現他的輸入/輸出範圍非常大,即使能將所有執行結果窮舉出來,但要將所有測試案例都執行一次顯然不符合測試經濟學,若能夠將軟體程式碼進行[Code Review](https://en.wikipedia.org/wiki/Code_review),會更輕易快速地找出問題。==白箱測試在難保證測試覆蓋率狀況下,可針對程式碼缺陷找出問題點較黑箱測試效率更高==。 > > > > > ⒉ 輸出結果是最佳解 > > ```md > > 今有導航系統計算兩點之間最快路徑 > > > > 測試動作: > > 1. 在地圖選取兩點讓導航計算出最快路徑 > > 2. 查看該路徑成果 > > > > 預期結果: > > 導航計算結果應是最快路徑 > > > > 測試結果: > > 發現除了導航算出最快路徑外,還有其他更快的路徑可以走 > > ``` > > > ㅤ最短路徑是一項[複雜的計算過程](https://web.ntnu.edu.tw/~algo/Path.html),如果軟體需要求最佳解的話,其最佳解可能在理論上是永遠無法求到,若為了驗證該軟體演算法而去開發另一個演算法來檢驗,顯然是非常不合測試經濟學,上描述例子來看應對程式碼所使用的演算法進行審核,查看是否是演算法本身缺陷或是程式碼撰寫不符。==白箱測試直接審核軟體程式碼與相關演算法,可以直接針對最佳化問題進行測試,比起黑箱測試針對輸入與輸出結果,較能找出問題並修復==。 > > > > > > > • **什麼時候最適合灰箱測試?** > > > > ⒈ 需要節省模擬環境測試成本 > > ```md > > 今有1000名玩家多人連線遊戲 > > > > 測試動作: > > 1. 可以成功登入1000名玩家 > > 2. 登入後進行遊玩 > > > > 預期結果: > > 確認1000名玩家登入狀況下,可以正常進行遊戲 > > > > 測試結果: > > 登入到500個玩家後,第501個玩家進入後開始,發現遊戲連線品質異常卡頓緩慢 > > ``` > > > ㅤ上述例子來看為了驗證該項目,使用1000台客戶端的硬體裝置成本顯然過於龐大,為了更好節省測試的硬體成本,可以使用無GUI畫面(另稱[Text-mode](https://en.wikipedia.org/wiki/List_of_text-based_computer_games))模式的客戶端程式進行連線,依上敘測試結果可先使用500位玩家做無GUI模式連線,在使用一位具備硬體設備以正常的GUI模式進行連線,並於遊戲中直觀方式感受連線品質,直到最終目標9999名玩家遊戲連線數後,最後一名玩家在以GUI模式進入驗證連線是否順暢。由於無GUI模式必須額外做軟體開發,必須對客戶端的軟體程式碼有一定的理解下才能實作,因此==灰箱測試可以極大的降低部分測試需求的硬體成本,並更好的模擬出錯誤所發生的情境==。 > > > > > ### ⒉功能測試分類:冒煙/健全/回歸/可用性 > • **功能測試分類分類簡介:** > > ㅤ功能測試目標是為了評估系統或元件是否符合指定的功能要求,是屬於實踐黑箱測試的一種方式,但並非完全不需要理解軟體架構。功能測試實作的對象並非是某片段程式碼所構成的單一功能模組,而是施作在一個完整系統上,即[未開發完整的系統將不適用功能測試](https://en.wikipedia.org/wiki/Functional_testing)。功能測試分類方式主要以執行對象量做分類:冒煙測試,健全性測試,回歸測試,可用性測試。 > > • *冒煙測試 (Smoke Testing)* > > > >ㅤ冒煙測試是針對軟件版本包進行詳細測試之前的預測試,主要目的是快速驗證軟件基本功能是否有缺陷,並非對該軟體版本的全面深入測試。如[《Lessons Learned in Software Testing》[2]](#👣參考書目)描述:「==冒煙測試僅僅是在短時間廣泛地覆蓋產品功能。如果關鍵功能無法正常工作或關鍵bug尚未修復,那麼你們的團隊就不需要浪費更多時間去安裝部署以及測試==」 > > > > ▼冒煙測試執行範例: > > ```md > > 今一會員系統加入新註冊用戶獎勵功能,程式環境部屬完成後執行冒煙測試: > > 1. 打開登入頁面確認版本是否正確 > > 2. 點擊登入頁面按鈕確認有正常反應 > > ``` > > • *健全性測試 (Sanity Testing)* > > > >ㅤ健全性測試是針對軟體的功能做合理性檢驗,除檢驗新功能是否預期運作外,==還需刻意輸入不合理參數(即[反向測試](#%E2%92%88%E6%AD%A3%E5%90%91%E6%B8%AC%E8%A9%A6%E8%B2%A0%E5%90%91%E6%B8%AC%E8%A9%A6))排除某些明顯錯誤的結果==,通常依[經驗法則](https://zh.wikipedia.org/zh-tw/%E7%B6%93%E9%A9%97%E6%B3%95%E5%89%87)或粗略計算預期結果以執行測試。 > > > > ▼健全性測試執行範例: > > ```md > > 今一會員系統加入新註冊用戶獎勵功能,執行以下健全性測試案例: > > 1. 新註冊帳號應可以正常註冊並領獎 > > 2. 新註冊帳號領獎完後,再登入應不能領取註冊獎勵 > > 3. 註冊新帳號應不能與舊帳號名稱重複 > > 4. 舊使用者登入後應不能領取註冊獎勵 > > ... > > ``` > > > • *回歸測試 (Regression Testing)* > > > >ㅤ回歸測試目的是==檢驗舊版本既有功能是否受到影響,驗證軟體新版本修改後整體功能的正確性==。其測試案例的設計通常要依新版本軟體更新進行同步,確保下次施作回歸測試結果能與上版本直接比照。 > > > > ▼回歸測試試執行範例: > > ```md > > 今一會員系統加入新註冊用戶獎勵功能,依舊版本測試案例執行回歸測試: > > 1. 確認舊使用者帳密可以正常登入,且使用者資料皆正確且合理 > > 2. 確認新註冊帳號可以正常註冊並登入 > > ... > > > > 註: 新註冊用戶領取獎勵屬於新功能,應先將新功能測試完成後續才施作回歸測試, > > ㅤㅤ新註冊用戶領取獎勵將加入下次測試案例,確保下次回歸測試正確性。 > > ``` > > > • *可用性測試 (Usability Testing)* > > > >ㅤ可用性測試是藉由通過使用者評測來評估產品是否滿足產品需求的方法,由於軟體問題並不一定是屬於錯誤,可用性測試獲取==使用者的真實使用體驗反饋,易找出難以在開發過程或早期測試難以發現的問題,因此不需要預先準備測試案例==。 > > > > ▼可用性測試執行範例: > > ```md > > 舉辦一產品封測會議,針對測試用戶群做出篩選,在用戶體驗結束後做出報告總結 > > > > 1. 本會議測試產品哪些功能? > > 2. 產品有沒有錯誤發生? 是已知錯誤嗎? > > 3. 產品是否符合設計者預期? > > 4. 用戶是否覺得產品符合他們預期? > > 5. 用戶有沒有提出建議優化方向? > > ``` > > * **功能性測試總整理:** > > ㅤ | **冒煙測試** | **健全性測試** | **回歸測試** | **可用性測試** > > -- | -- | -- | -- | -- > > **執行次序** | 1 | 2 | 3 | 4 > > **測試耗時** | 最快 | 快 | 慢 | 不一定 | > > **驗證目標** | 部屬是否成功? | 新功能是否正常? | 舊功能是否正常? |產品是否符合需求? | > > > > *依上表可理解功能性測試是需要依次序循序漸進施作 > > ### ⒊模組化測試分類:單元/整合/系統/端對端/驗收 > • **測試方法比較:** > >ㅤ | **單元測試** | **整合測試** | **系統測試** | **端對端測試** | **驗收測試** > -- | -- | -- | -- | -- | -- | > **執行次序** | 1 | 2 | 3 | 4 | 5 | > **測試對象** | 程式碼函式 | 函式庫或子程式 | 程式組合/子系統 | 完整系統+模擬數據 | 完整系統+上線數據 | > **測試環境** | 開發環境(DEV) | 開發環境(DEV) | 測試環境(QAT)| 測試或模擬環境(QAT/UAT)| 模擬或正式環境(UAT/PRD) | > **測試執行者** | Developer | Developer/IT | SDET | Tester | User | > > • *單元測試 (Unit Testing)* > > > >ㅤ==以程式碼中的函式最小單位進行測試,確保城程式碼修改後繼有功能不會遭破壞==,通常由發人員自行撰並於建構執行檔(build/compiler)時執行測試。 > > > > 單元測試需要預先定義函式輸入與輸出結果,在執行測試過程中比對結果,測試結果僅有: ``成功``/``失敗`` 兩種結果,以下為javascript實作單元測試程式碼範例: > > > > ```javascript > > //greeting為一函式 > > //主要功能是輸入'名稱'、'姓氏'後回傳歡迎訊息 > > const greeting = (firstName, lastName) => { > > return `Hello Mr./Mrs. ${firstName} ${lastName}` > > } > > ``` > > ```javascript > > //test為執行單元測試 > > //主要驗證輸入'名稱'、'姓氏'後回傳歡迎訊息是否符合預期 > > test('should output firstName and lastName', () => { > > const text = greeting('Moataz', 'Mahmoud') //Moataz=名稱,Mahmoud=姓氏 > > expect(text).toBe('Hello Mr./Mrs. Moataz Mahmoud') //回傳結果應為 'Hello Mr./Mrs. Moataz Mahmoud' > > }) > > ``` > > > • *整合測試 (Integration Testing)* > > > >ㅤ==整合測試主要將程式碼中的函式或函式庫模組化,測試模組與模組之間相連後執行是否異常==,通常在系統部屬前執行測試。 > > > > > > 整合測試需要預先定義期望的輸出結果,於執行過程中對模組接口輸入所需資料,輸入完成後再進行結果比對,以下為javascript整合測試程式碼範例: > > > > ```javascript > > //此為一API整合測試執行範例 > > const co = require('co'); //設置測試輸入請求參數 > > const test = require('blue-tape'); //設置測試輸入請求參數 > > const factory = require('factory'); //設置測試輸入請求參數 > > const superTest = require('../utils/super_test'); //測試項目的預期結果 > > const testEnvironment = require('../utils/test_environment_preparer'); //讀取測試環境參數 > > const path = '/v1/admin/recipes'; //設置測試API路徑 > > > > test(`API GET ${path}`, co.wrap(function* (t) { > > yield testEnvironment.prepare(); //讀取測試環境參數 > > const recipe1 = yield factory.create('recipe'); > > const recipe2 = yield factory.create('recipe'); > > const serverResponse = yield superTest.get(path); //取出預期結果 > > > > t.deepEqual(serverResponse.body, [recipe1, recipe2]); //比對測試結果是否符合預期 > > })); > > ``` > • *系統測試 (System Testing) vs 端對端測試(E2E Testing)* > > > > * *系統測試* : ==將撰寫好的程式進行部屬,測試各子專案軟體在部屬後是否存在異常==。又稱組裝測試可以針對子專案的不同版本進行組合,用以確認各版本之間相容性。 > > * *端對端測試* : ==依使用者角度進行操作,即用戶端到系統底層端的完整流程測試,確認產品運行是否存在無法預期的問題==。 > > > > ▼ 系統測試與端對端測試不同處: > > > ![](https://raw.githubusercontent.com/DukeHuangWP/HackMD/master/%E6%88%91%E7%9A%84%E8%BB%9F%E9%AB%94%E6%B8%AC%E8%A9%A6%E6%A6%82%E8%AB%96/System%20Testing%20vs%20E2E%20Testing.svg) > > > > ▼ 系統測試可避免開發者只專注單部件,而忽略了系統流程: > > > ![](https://raw.githubusercontent.com/DukeHuangWP/HackMD/master/%E6%88%91%E7%9A%84%E8%BB%9F%E9%AB%94%E6%B8%AC%E8%A9%A6%E6%A6%82%E8%AB%96/%E7%B3%BB%E7%B5%B1%E6%B8%AC%E8%A9%A6.gif) > > > > ▼ 端對端測試易發現產品設計缺陷,找出難以預期的錯誤結果: > > > ![](https://raw.githubusercontent.com/DukeHuangWP/HackMD/master/%E6%88%91%E7%9A%84%E8%BB%9F%E9%AB%94%E6%B8%AC%E8%A9%A6%E6%A6%82%E8%AB%96/E2E%E6%B8%AC%E8%A9%A6.gif) > > ㅤㅤ > > > • *驗收測試 (Acceptance testing)* > > > >ㅤ驗收測試是最終使用者(客戶、客服、運營人員...)針對最終產品進行的驗收,==其系統軟硬體規格必須正式環境相當,甚至可在正式上線產品上直接做測試==。主要目的是驗證產品是否滿足最終使用者需求,評估客戶滿意程度並制定產品改進目標。 > > > > ▼ 即使產品符合設計也通過了各階段測試,仍可能無法符合客戶的需求: > > > ![](https://raw.githubusercontent.com/DukeHuangWP/HackMD/master/%E6%88%91%E7%9A%84%E8%BB%9F%E9%AB%94%E6%B8%AC%E8%A9%A6%E6%A6%82%E8%AB%96/%E9%A9%97%E6%94%B6%E6%B8%AC%E8%A9%A6.gif) > > ## Ⅱ.👊非功能性測試(Non-functional) ### ⒈性能測試量分類:負載/壓力/健康 > • *性能測試(Performance testing)* > > > > ㅤ在不同的參數或是負載情況底下對於系統、API 接口的效能、穩定性、可靠性、速度、資源使用情況的評估分析。測試之前可以先預估,未來系統上線時所需要承受的用戶數量,響應時間,系統資源使用情況…等規格。測試執行完成之後,對於測試報告所記錄的數據,再去分析、調整系統之所需,達到驗收時所需要的標準。Ex: 用戶數量、每秒點擊次數、系統響應時間、延遲時間…等。 > > > • *負載測試(Load testing)* > > > > ㅤ在系統、應用程序或網站模擬對於在正常流量或大量用戶在一段時間內對於系統操作的測試。通常用於系統或網站開發完成時,需要對系統評估系統可否乘載大量使用者的評估,或者大量用戶同時使用時,系統還能夠處於穩定的狀態。 > > > • *壓力測試(Stress testing)* > > > >ㅤ壓力測試是一種系統穩定、可靠性測試,屬於非功能性測試,在極端環境負載的狀況下進行測試系統的上限。Ex: 系統CPU、Memory > > 在極端負載情況下的表現,是否有緩慢、數據遺失損壞、Memory leak …等情形。 ### ⒉軟體發布順序分類:Alpha/Beta > ## Ⅲ.🗣其他測試(Other) ### ⒈相容性測試 ### ⒉A/B測試 ### ⒊本地化測試和國際化測試 ⏲撰寫測試案例(Test case) === > 測試案例又翻譯為測試用例,可分為*可形式化(Formal test cases)*和*非形式化(Informal test cases)*,因並非所有測試都可以預先規劃出明確的[測試準則](#•-測試準則Test-Oracle),此類型被稱為非形式化,反之可形式化的測試案例可參考[測試方法當中-可形式化測試案例](🔮可形式化測試案例(Formalized))說明。 > 依照[artoftesting.com](https://artoftesting.com/test-case),將定義精簡畫其測試案例應具備: > > 1. **測試編號(TestCaseId)** > > > > 測試案例唯一編號,用來區分與統計測試項目使用。 > > 2. **內容描述(Description)** > > > > 主要描述該測試項目主要目的,以及與該測試相關文件。 > > 3. **先決條件(Precondition)** > > > > 執行該測試項目前應預先設置條件或參數設定。 > > 4. **操作步驟(Test Steps)** > > > > 執行該測試項目的具體步驟。 > > 5. **預期結果(Expected result)** > > > > 依據產品規格定義預期結果。 > > 6. **實際結果(Actual result)**``[執行後填寫]`` > > > > 執行該測試項目後的實際結果。 > > 7. **測試結果判定(Test Result)** > > > > 比較預期結果和實際結果做出判斷,即[測試準則](#•-測試準則Test-Oracle)判斷該*測試通過(pass)*與*不通過(fail)*。 > > 8. **執行日期(Date)**``[執行後填寫]`` > > > > 紀錄執行該測試項目的時間。 > > 9. **執行測試者(Executed by)**``[執行後填寫]`` > > > > 紀錄執行該測試人員,以便未來追蹤測試案例。 > > 10. ☐**自動化狀態(Automation Status)**``[執行後填寫]`` ``[非必要]`` > > > > 紀錄是否可施作自動化,或已自動化完成。 🏃測試驗證與檢查(Test Verification and Validation) === ㅤ依[Jiantao Pan[4]](#👣參考書目)所提出良好的程式設計是一種容易進行驗證、修改和維護的設計。因測試是一項嚴謹的工作需要大量的時間和成本,所以設計測試性是軟件開發的重要設計原則之一。 ## 💹優化測試執行效率 ### ⒈正向測試/負向測試 > ㅤ*正向測試(Positive Testing)/負向測試(Negative Testing)* 是提升執行測試效率與品質的一種方法。正向測試是依產品預期功能所做的操作步驟,確認產品使用上是否符合預期;負向測試則相反依非產品預期功能所做的操作步驟,確認產品使用上不會有錯誤崩潰狀況,確保專案能永久的持續運行。 > > • 正向測試/負向測試執行範例1: **使用者資料輸入功能頁** > > ㅤ | 測試步驟 | 正向測試結果 > > -- | -- | -- > > **正向測試** | 輸入正確的E-Mail格式 | 輸入完成可以正常儲存使用者資料 > > **反向測試** | 輸入無效的E-Mail格式 | 輸入完成可以正常儲存使用者資料 > > > > 以上測試案例當中反向測試**輸入無效的E-Mail格式**,其測試結果**輸入完成可以正常儲存使用者資料**屬於不合理的測試結果,因使用者E-Mail資料可能會用於系統寄送重要通知,若**無效的E-Mail格式**被寫入系統,未來系統運行時可能會引發崩潰等錯誤狀況。==此時反向測試**輸入無效的EMail格式**合理的結果應為**輸入完成後回傳無效的E-Mail格式**,並且無法儲存使用者資料==。 > > • 正向測試/負向測試執行範例2: **圖片上傳功能頁** > > ㅤ | 測試步驟 | 正向測試結果 > > -- | -- | -- > > **正向測試** | 將`.jpg`、`.png`、`.gif`檔案格式上傳 | 回傳上傳成功 > > **反向測試** | 將`.sql`、`.exe`、`.sh`檔案格式上傳 | 回傳上傳失敗 > > > > 以上測試案例當中反向測試上傳``.sql``、``.exe``、``.sh``檔案格式,其測試結果``回傳上傳失敗``屬於合理的結果。因上傳檔案可能會夾帶病毒讓系統遭受攻擊引發系統崩潰,由於資安技術範圍無邊際,==產品規格書無法詳細窮舉所有檔案格式,測試案例制定時應適當加入反向測試確保系統安全==。 > > • 正向測試/負向測試執行步驟範例: > > ```cmake > > 今有存款系統,單次存入最大上限為$500.00 ,最小限制為$0.01 > > > > 以下為測試執行步驟與結果: > > 1. 進行正向測試 : 存入$1.00 → 測試結果正常存入且餘額符合預期 > > 2. 進行正向測試 : 存入$0.01 → 測試結果正常存入且餘額符合預期 > > 3. 進行反向測試 : 存入$0.001 → 測試結果可存入且餘額加總正確,但低於0.01不符合預期 > > 4. 進行反向測試 : 存入$-1.00 → 測試結果可存入且餘額遭到扣除,但存入負數不符合預期 > > 5. 進行反向測試 : 存入$-2.00 → 測試結果可存入且餘額遭到扣除,但存入負數不符合預期 > > 6. 進行正向測試 : 存入$2.00 → 測試結果正常存入且餘額符合預期 > > 7. 進行正向測試 : 存入$500.00 → 測試結果正常存入且餘額符合預期 > > 8. 進行正向測試 : 存入$501.00 → 測試結果回傳失敗且餘額不變,符合預期 > > ... > > ``` > > > > 觀察測試案步驟可以發現,正向測試存入範圍為`$0.01~500.00`之間,其餘輸入值皆為反向測試。其中執行步驟`3.`發現錯誤後,其步驟`6.`可以省略不執行;其中執行步驟`4.`發現錯誤後,其步驟`5.`可以省略不執行。 > > > > * *為何`5.`、`6.`、`7.`可以省略不執行?* > > > > 1. 執行步驟`3.`測試案例小於$0.01金額不應存入,即使`6.`、`7.`步驟執行符合預期,==只要區間判斷有任何錯誤,開發者必需將整區間判斷函式重新編寫==,`6.`、`7.`步驟可在前置反向測試通過後再執行剩餘正向測試效益較高。 > > 2. 執行步驟`4.`測試案例負數金額不應存入,但執行結果意外發現存款系統引發扣款,該功能與規格描述不符合,因考量到 [軟體測試經濟學](#%E2%80%A2-%E8%BB%9F%E9%AB%94%E6%B8%AC%E8%A9%A6%E7%B6%93%E6%BF%9F%E5%AD%B8The-Economics-of-Testing),其餘負數存入金額可以忽略不執行(步驟`5.`),==在前置測試結果不符預期狀況,可忽略後續相同測試案例的反向測試步驟,增加專案執行測試效率==。 ## ✍測試技巧與實際案例 ㅤ測試計畫(Test Plan)與測試案例(Test Case)於擬定和執行階段可依賴[經驗法則](https://zh.wikipedia.org/zh-tw/%E7%B6%93%E9%A9%97%E6%B3%95%E5%89%87),技巧性的利用開發盲區提高測試效率,以下是常見的軟體設計缺陷案例。 ### ⒈數值運算 > > > > • 小數精度與無理數問題 > > ```javascript > > console.log(1/3); > > //執行除法結果為 0.3333333333333333 > > //實際上1÷3結果為一無理數,無法以小數完成呈現0.3333333333333333....... > > ``` > > ㅤ==若測試功能有小數除法存在,則規格必須先定義小數精度==(例如:**小數第二位後四捨五入**),否則經過多次二進位運算小數除法與加總後必失真,其結果往往與習慣十進位的使用者的認知不同。 > > ㅤ > > • 浮點數運算問題 > > ```javascript > > console.log(0.1+0.2); > > //執行加總結果為 0.30000000000000004 > > ``` > > ㅤ上面運算結果為程式語言常見的[浮點數運算問題](https://0.30000000000000004.com/),在測試前可以先了解軟體編寫語言,預先查詢是否存在浮點數運算問題。==不妨把直接0.1+0.2進行驗證是否結果為0.3== > > ㅤ > > • 數值溢位問題(overflow) > > ```javascript > > var max = Number.MAX_VALUE; > > console.log("Numer最大整數值 : " + max); > > console.log("Numer最大整數值+1 : " + (max+1)); > > //Numer最大整數值 : 1.7976931348623157e+308 > > //Numer最大整數值+1 : 1.7976931348623157e+308 > > //Number.MAX_VALUE+1會引發overflow,最大值再加總後值前後未改變 > > ``` > > > > ```javascript > > #include <stdio.h> > > #include <limits.h> > > > > int main() { > > printf("最大整數值 : %d\n",INT_MAX); > > printf("最大整數值+1 : %d\n",INT_MAX+1); > > return 0; > > } > > //Numer最大整數值 : 2147483647 > > //Numer最大整數值+1 : -2147483648 > > //INT_MAX+1會引發overflow,原本最大正整數變成最小負整數 > > ``` > > ㅤ因二進位硬體分配記憶體空間並非無限大,[不同程式語言對於變數皆有固定的值範圍](https://en.wikipedia.org/wiki/Integer_(computer_science)),==在測試功能包含數值運算時,不妨使用極值作為輸入輸出進行反向測試==: > > > > ▼常見的變數極值(最大值/最大值) > > __________________________| 最小值 | 最大值 > > -- | -- | -- > > Unigned 8-bit Integer | `0` | `255` > > Unigned 64-bit Integer | `0` | `18446744073709551615` > > Unigned 128-bit Integer | `0` | `340282366920938463463374607431768211455` > > Signed 8-bit Integer | `-128` | `127` > > Signed 16-bit Integer | `-32768` | `32767` > > Signed 32-bit Integer | `−2147483648` | `2147483647` > > Signed 64-bit Integer | `−9223372036854775808` | `9223372036854775807` > > Signed 128-bit Integer | `−170141183460469231731687303715884105728` | `170141183460469231731687303715884105727` > ㅤ ### ⒉文字輸入 > > > > • 特殊語言的字母 > > 簡介 : 由於*Unicode*編碼應用廣泛,==部分特殊字元可能會造成產品顯示上的凸框或破圖==。 > > ㅤ | 範例 | 說明 > > -- | -- | -- > > 泰國韻母調類符號 | `้้้้้้้้` (8個字)|[泰國韻母調類符號](https://zh.wikipedia.org/zh-tw/%E6%B5%B7%E5%A7%86%E5%AE%B0‬) > > 由右至左書寫語言 | `ء` | [阿拉伯文字](https://zh.wikipedia.org/zh-tw/%E6%B5%B7%E5%A7%86%E5%AE%B0‬) > > ㅤ > > • 作業系統檔案名稱規則 > > 簡介 : 具備上傳或下載檔案產品的需求,可能因作業系統有檔案名稱規則引發錯誤。 > > > > 測試方式參考: > > 1. 作業系統檔案名稱規則以[*Windows最為嚴格*](https://en.wikipedia.org/wiki/Filename)。 > > 2. 建立一個`?%*|"<>;\/`的檔案名稱,然後再將檔案上傳到系統中測試(可使用*MacOS*進行操作)。 > > 3. 伺服器所提供給客戶端的檔案檔名不應包含`?%*|"<>;\/`當中任何字元。 > > 4. 下載`zip`、`rar`..等壓縮包內不應包含`?%*|"<>;\/`當中任何字元的檔案名稱。 > > ㅤ > > • 作業系統檔案名稱與路徑長度限制 > > 簡介 : 具備上傳或下載檔案產品的需求,可能因作業系統有檔案名稱長度限制引發錯誤。 > > 規範 | 作業系統 | ______________________________________ | > > -- | -- | -- > > 檔案名稱限制不得超過`255`字元 | MacOS | 詳見[Apple開發者文檔](https://developer.apple.com/library/archive/technotes/tn/tn1150.html) > > 檔案路徑長度限制不得超過`260`字元 | Windows | 詳見[微軟針對Windows API開發文檔](https://learn.microsoft.com/zh-tw/windows/win32/fileio/maximum-file-path-limitation?tabs=registry) > > > > *其餘作業系統限制可參考[IBM各作業系統文檔](https://www.ibm.com/docs/en/spectrum-protect/8.1.9?topic=parameters-file-specification-syntax) > > > > 測試方式參考: > > 1. 建立一個`255`字元長度的檔案名稱,然後再將檔案上傳到系統中測試。 > > 2. 若產品有處理`zip`、`rar`..等壓縮包功能,建立一個內含 ==255字元長度的檔案名稱== 並放入 ==260字元長度的路徑資料夾內== 的壓縮檔並上傳。 > > 3. 下載`zip`、`rar`..等壓縮包時,需檢查解壓縮路徑==是否超過260字元長度==,避免使用者無法解包。 > > ㅤ > > • XSS跨網站指令碼(Cross-site scripting) > > > 簡介 : XSS常見的網路瀏覽器資安問題,==若產品需要使用到瀏覽器應XSS將納入測試案例==。 > > > > 基本原理: 客戶端發送請求中夾帶惡意代碼,讓網站伺服器儲存該惡意代碼,讓其他客戶端存取該資訊時執行該段代碼。 > > 分類 | 範例 | _________________________________________________________________ > > -- | -- | -- > > | 反射型Stored | 輸入聊天室訊息 | ==<script>alert("伺服器爆炸了");</script>== > > | 儲存型Reflected | 留言板存入訊息 | `<iframe src="http://hacker.com/index.html" sandbox></iframe>` > > > > 測試方式參考: > > 1. 若產品URL路徑有使用到Query字串,可以嘗試對URL嘗試XSS指令,例如: ==https://example.com/run?call=<script>alert("XSS");</script>==,查看瀏覽器是否跳出XSS彈窗。 > > 2. 若產品包含**聊天室**...廣播使用者訊息功能時,應嘗試送出XSS指令當作訊息,同時觀察其他使用者瀏覽器是否收到攻擊訊息。 ㅤ > > 3. 若產品包含**留言板**、**修改暱稱**...顯示使用者自訂訊息功能時,應嘗試送出XSS指令後再使用瀏覽器重新進入頁面查看攻擊訊息是否遭執行。 > > ㅤ > > • 資料庫注入攻擊(Database injection) > > > 簡介 : 資料庫注入攻擊又另一稱呼[*SQL注入(SQL injection)*](https://zh.wikipedia.org/zh-tw/SQL%E6%B3%A8%E5%85%A5),是一種常見的伺服器攻擊方式。 > > > > 基本原理 : 產品只要有使用到資料庫皆可以進行此類測試,常見注入指令格式為*SQL*。 > > > > • MySQL類型常見注入攻擊指令: > > > > > 指令 | 說明 | > > > -- | -- | > > > | `'` | SQL數值引號 | > > > | `--` | SQL數值引號註解號| > > > | `OR` | 代入`WHERE`中避開其他查詢條件 | > > > | `1=1` | 代入`WHERE`中避開其他查詢條件 | > > > > > > ▼執行範例 : =='OR 1=1 --== > > > ```sql > > > /* 原先傳入到資料庫指令 */ > > > SELECT * FROM customers WHERE name ='使用者名稱' AND password = '使用者密碼' > > > > > > /* 在攻擊者將'使用者名稱'替換成 → 使用者名稱'OR 1=1 -- 後變成以下, > > > 密碼輸入將被資料庫忽略並取出帳號相關資料 */ > > > SELECT * FROM customers WHERE name ='使用者名稱' OR 1=1 -- password = '不需要密碼' > > > ``` > > • Transact-SQL類型常見注入攻擊指令: > > > > > 指令 | 說明 | > > > -- | -- | > > > | `;EXEC` | Transact-SQL執行其他程序指令 | > > > > > > ▼執行範例 : ==;EXEC master.dbo.xp_cmdshell== > > > ```graphql > > > # 利用 ;EXEC master.dbo.xp_cmdshell cmd.exe /c 對資料庫作業系統Windows下達cmd指令 > > > # 在瀏覽器網址中注入隱碼後,伺服器再將指令傳送給資料庫執行 > > > http://example.com/show.asp?id=1;EXEC master.dbo.xp_cmdshell cmd.exe /c msg %username% "SQL injection" > > > ``` <div style="opacity:0;"> ### ⒉隱藏數據測試 隱藏數據測試在軟體驗收和確認階段是十分必要和重要的一部分。程式的質量不僅僅通過用戶界面的可視化數據來驗證,而且必須包括遍歷系統的所有數據。 假設一個應用程式要求用戶兩條信息-----用戶名和密碼來創建帳戶。這個用戶輸入這兩條數據後保存。最後,一個確認視窗將通過資料庫中找到這條數據來顯示用戶名和密碼給用戶。為了驗證所有的數據保存是否正確,一個QA測試人員會在這個確認視窗簡單的查看下用戶名和密碼。如果他們成功了?假設資料庫記錄了第三條信息----創建日期,它可能不會出現在確認視窗,而只在存檔中才出現。如果創建日期保留的不正確,而QA測試人員只驗證螢幕上的數據,那么這個問題就不可能被發現。創建日期可能就是一個bug,由於一個用戶帳戶保存了一個錯誤的日期到資料庫中,這個問題也不可能會被引起注意,因為它被用戶界面所隱藏。這只是一個簡單的例子,但是它卻演化出了一點:隱藏數據測試的重要性。 等價劃分測試的英文是equivalence partition testing。 等價劃分測試是根據等價類設計測試用例的一種技術。是黑盒測試的典型方法之一,通過把被測試程式所有可能的輸入數據域劃分成若干部分。從每一部分中選取少數有代表性的數據作為測試用例,可有效減少測試次數,極大提高軟體測試效率,縮短軟體開發周期.等價類劃分測試的目的就是為了在有限的測試資源的情況下,用少量有代表性的數據得到比較好的測試效果。有效等價類和無效等價類。有效等價類中的數據代表的是一組符合需求文檔的正確的有意義數據。無效等價類則正相反。 文檔測試的英文是documentation testing,測試關注於文檔的正確性。 判定表的英文是decision table,是指一個表格,用於顯示條件和條件導致動作的集合。 定義:判定表是分析和表達多邏輯條件下執行不同操作的情況的工具。 判定表的優點:能夠將複雜的問題按照各種可能的情況全部列舉出來,簡明並避免遺漏。因此,利用判定表能夠設計出完整的測試用例集合。 在一些數據處理問題當中,某些操作的實施依賴於多個邏輯條件的組合,即:針對不同邏輯條件的組合值,分別執行不同的操作。判定表很適合於處理這類問題 </div> # 🪬測試計畫制定(Test plan) ㅤ是一個為了確保軟體品質而進行的標準化測試過程。它定義了從測試開始到結束的具體步驟,確保測試工作是有計畫、有系統地進行,而不是隨意地「找 Bug」。 ## ♻️軟體測試生命週期 (STLC) 說明 > 簡單來說,[STLC軟體測試生命週期](https://aws.amazon.com/tw/what-is/sdlc/)就是測試團隊的「工作指南」,告訴他們什麼時候該做什麼事: > > 1. **測試編號(TestCaseId)** > > > > 測試案例唯一編號,用來區分與統計測試項目使用。 > > 2. **內容描述(Description)** > > > > 主要描述該測試項目主要目的,以及與該測試相關文件。 > > 3. **先決條件(Precondition)** > > > > 執行該測試項目前應預先設置條件或參數設定。 > > 4. **操作步驟(Test Steps)** > > > > 執行該測試項目的具體步驟。 > > 5. **預期結果(Expected result)** > > > > 依據產品規格定義預期結果。 > > 6. **實際結果(Actual result)**``[執行後填寫]`` > > > > 執行該測試項目後的實際結果。 > > 7. **測試結果判定(Test Result)** > > > > 比較預期結果和實際結果做出判斷,即[測試準則](#•-測試準則Test-Oracle)判斷該*測試通過(pass)*與*不通過(fail)*。 > > 8. **執行日期(Date)**``[執行後填寫]`` > > > > 紀錄執行該測試項目的時間。 > > 9. **執行測試者(Executed by)**``[執行後填寫]`` > > > > 紀錄執行該測試人員,以便未來追蹤測試案例。 > > 10. ☐**自動化狀態(Automation Status)**``[執行後填寫]`` ``[非必要]`` > > > > 紀錄是否可施作自動化,或已自動化完成。 ## 👓STLC 與 SDLC (軟體開發生命週期) 的關係說明 > **SDLC (Software Development Life Cycle)** 是涵蓋軟體從「誕生」到「廢棄」的全過程,包含規劃、開發、測試、部署和維護。 > **STLC 是 SDLC 的一部分**,兩者是「包含」與「並行」的關係: > > 1. **子集關係 (Subset):** STLC 專注於 SDLC 中的「測試」階段。但在現代開發模式中,STLC 的活動不僅僅發生在編碼之後,而是貫穿整個 SDLC。 > > 2. **並行關係 (Parallelism - V-Model):** 在標準的 V-Model 開發模型中,開發 (SDLC) 的每一個階段都有對應的測試 (STLC) 階段。例如: > * 當開發在做「需求分析」時,測試團隊就在做「驗收測試計畫」。 > * 當開發在做「系統設計」時,測試團隊就在做「系統測試計畫」。 > * 這確保了開發和測試是同步前進的,而不是等程式碼寫完才開始想怎麼測。 > > • 比較表:SDLC vs. STLC > > 為了讓你更清楚它們的區別,可以參考下表: > > | 比較項目 | SDLC (開發生命週期) | STLC (測試生命週期) | > > | --- | --- | --- | > > | **主要目標** | 成功**建構**出符合需求的軟體。 | 成功**驗證**軟體是否無誤且符合需求。 | > > | **核心心態** | 「如何做出來?」(`Creation`) | 「這東西對嗎?」(`Verification` & `Validation`) | > > | **主要執行者** | 開發人員 (`Developers`)、`PM` | 測試人員 (`Testers` / `QA`) | > > | **產出物** | 可運行的軟體系統。 | 測試報告、Bug 列表、測試案例。 | > > | **先後順序** | 這是整體的母集合。 | 這是其中的子集合或並行流程。 | > > > > **總結來說:** SDLC 負責「生出」軟體,而 STLC 負責確保生出來的軟體是「健康」的。 ## 🥊嚴重程度 (Severity) VS 優先順序 (Priority) 這兩個術語在軟體測試與 Bug 追蹤中經常被混用,但它們代表的意義截然不同。簡單來說:**嚴重程度 (Severity) 是「技術視角」,優先順序 (Priority) 是「商業視角」。** > ### 1. 核心定義比較 > > > | 比較項目 | **嚴重程度 (Severity)** | **優先順序 (Priority)** | > > | --- | --- | --- | > > | **定義** | 這個 Bug 對系統功能的**破壞程度**有多大? | 這個 Bug 需要**多快**被修復? | > > | **關注點** | **技術影響** (Technical Impact) | **商業價值** (Business Value) | > > | **衡量標準** | 功能是否癱瘓?資料是否遺失?有無替代方案? | 影響多少用戶?是否影響發布時程?是否損害品牌形象? | > > | **決定者** | 通常由 **測試人員 (QA)** 判定 | 通常由 **產品經理 (PM)** 或客戶判定 | > > | **隨時間變化** | **不會改變** (Bug 的技術破壞力是固定的) | **會改變** (隨專案時程或市場需求調整) | > > ### 2. 四種組合情境 (Severity vs. Priority Matrix) > > 最能體現兩者差異的,就是當它們「不一致」的時候。我們可以將 Bug 分為四個象限: > > #### 🔴 高嚴重,高優先 (High Severity, High Priority) > > **「系統炸了,且必須馬上修。」** > > > * **情境:** 使用者點擊「結帳」按鈕後,App 直接閃退,無法付款。 > > * **原因:** 功能完全癱瘓 (高嚴重),且直接阻礙營收 (高優先)。 > > #### 🟠 低嚴重,高優先 (Low Severity, High Priority) > > **「功能沒壞,但為了面子必須馬上修。」** > > > * **情境:** 公司首頁的 Logo 放反了,或是公司名稱拼錯 (例如把 `Google` 拼成 `Googlr`)。 > > * **原因:** 這不影響網站運作 (低嚴重),但嚴重損害品牌形象,老闆要求立刻改 (高優先)。 > > #### 🔵 高嚴重,低優先 (High Severity, Low Priority) > > **「功能壞了,但沒人會用到,或是可以先忍著。」** > > > * **情境:** 在一個已經宣佈停止支援的舊版瀏覽器 (如 IE8) 上,某個次要功能會導致當機。 > > * **原因:** 技術上這是個大 Bug (高嚴重),但因為幾乎沒有用戶使用該瀏覽器,修復它的商業價值極低 (低優先)。 > > #### 🟢 低嚴重,低優先 (Low Severity, Low Priority) > > **「小瑕疵,有空再修。」** > > > * **情境:** 「關於我們」頁面中的某個文字排版稍微歪了一點點。 > > * **原因:** 不影響功能 (低嚴重),也不太有人會注意 (低優先)。 > ### 3. 常見的誤解 > > > * 「嚴重的 Bug 一定要馬上修」 > > > 不一定。如果離上線只剩 1 小時,我們可能會選擇放生一個「很少觸發的嚴重 Bug」(High Severity, Low Priority),而優先修復一個「所有人都看得到的錯字」(Low Severity, High Priority)。 > > * 「工程師應該只看嚴重程度修 Bug」 > > > 工程師通常是依據 **優先順序 (Priority)** 來排定工作列表,因為這是團隊當前的目標。