C#_建立單元測試_QA_vs_QE_單元測試要做捨麼用?_Mock 物件??_stub 物件??_假物件???

【單元測試的用意即目的】

單元測試,是developer最該寫的測試程式,卻往往也是最容易被忽略的測試。


大家常碰到的測試相關問題是:

(1).往往一堆人寫測試程式時,自以為是在寫單元測試,卻壓根就不是單元測試,
而是整合測試。


(2).Production code是我寫的,如果測試程式也是我寫,那有什麼意義?
所以應該給 QA/QE來寫才能測出盲點。

【QA: 質量保證(Quality Assurance)】
主要是保證生產過程受控或保證產品合格,著重於維護】

在ISO8402:1994中的定義是“為了提供足夠的信任表明實體能夠滿足品質要求,而在品質管理體系中實施並根據需要進行證實的全部有計劃和有系統的活動”。

有些推行ISO9000的組織會設置這樣的部門或崗位,負責ISO9000標準所要求的有關品質保證的職能,擔任這類工作的人員就叫做QA人員

在CMMI中QA的主要工作是質量評審和產品評審


【QE : 品質工程師(Quality Engineer)】



(3).我程式都寫完了,跑起來也都對,這時寫測試程式一點意義都沒有。

(4).測試程式要跑好久。

(5).沒有測試環境,要怎麼寫測試。



看完這幾篇單元測試的相關文章後,希望大家可以獲得一些想法,解決這些問題。





Why

先舉幾個在開發上常見的問題:
Dn194488.F1F8F0F2B05A7291DFA30F7CD9C16FF8(zh-tw,MSDN.10).png
  1. 怎麼讓UI, Service, Data Access等不同模組之間可以平行開發?
  2. 要到真實環境方能測試程式無誤
  3. 頁面發生錯誤,到底是誰錯了?
  4. 交付的程式,到底測過哪些東西了?
  5. 我改了這支程式,會不會害其他程式掛掉?
以上這些問題,能透過哪些Unit Test相關的方式來解決呢?
Dn194488.F4BC798F733D518B6E163354DD2344BC(zh-tw,MSDN.10).png
  1. Unit Test中使用stub/mock object,達到關注點分離
  2. Unit Test使用stub/mock object來模擬外部回傳的資料
  3. 把input值當作test case,跑一次Unit Test
  4. 交付的程式要包含Unit Test程式碼
  5. 改完程式就跑一次Unit Test吧!


Mock 物件(Mock Object) 所代表的是一個「模仿的物件」,
是假物件(Fake Object) 的一種,總之不是真的物件就對了

為什麼要有這些假物件的存在????

這些假物件的主要目的在於協助單元測試程式可以順利執行,並且讓單元測試專案裡的測試方法 (Test Method) 在執行測試時可以測試我們想測試的部分就好。




單元測試是軟體測試的最小單位,如果測試的範圍輕易的就會擴展到其他類別或同類別的其他方法,那就不再是最小單位,也就不是單元測試了!
【習自: link



總而言之,沒有被測試涵蓋到的程式碼,即使它可能是對的,也沒人敢拍胸脯保證。而有了測試案例來輔助說明與保護,至少可以拍胸保證,在這樣的測試案例下,這個物件的設定,肯定如同預期般執行。
而單元測試可以提供迴歸測試的保護,在每一次異動完程式,可以單鍵執行就知道是否破壞了原本對物件行為的預期。
回歸測試:
是指修改了舊代碼後,重新進行測試以確認修改沒有引入新的錯誤或導致其他代碼產生錯誤。
 自動回歸測試將大幅降低系統測試、維護升級等階段的成本。


單元測試可以透過一些輔助設計,來達到與外部環境、服務、相依隔絕,而僅測試該物件本身的邏輯,以及與外部的互動是否符合預期。
注意!造成問題的測試案例,往往是最珍貴的,因為最具代表性,也最具價值。因為它提供了我們修正bug的方向以及指標。而針對發生問題的測試案例,來執行單元測試,馬上就可以知道是否是該物件的內部問題。
最後,單元測試由於具備與外界服務、相依隔絕的特性,所以可以幫助撰寫實際的物件時,具有可測試性、低耦合性,彼此之間只相依於抽象或介面。進而透過IoC的設計,讓我們可以做到關注點分離,讓開發各個物件的developer,可以透過介面來溝通,當能不直接相依於彼此的實作細節,就能平行開發。

What

Unit Test的定義與基本準則,如下圖所示:
Dn194488.DC78FD30487B5CF9FA6CFC2BEB33FA70(zh-tw,MSDN.10).png
  1. 一次只測試一種測試案例,只測試目標物件一種方法
  2. 最小的測試單位
  3. 不與外部(包括檔案、資料庫、網路、服務、物件、類別)直接相依
  4. 不具備邏輯
  5. 測試案例之間相依性為零
Unit Test的特性,一個字:FIRST。如下圖所示:
Dn194488.83230CE1912CB7F307E1226ED75BA0D5(zh-tw,MSDN.10).png
  1. Fast:快速。
  2. Independent:獨立。
  3. Repeatable:可重複。
  4. Self-Validating:可反應驗證結果。單元測試不論成功或失敗,都應該要從測試的 reporting 直接瞭解其意義或失敗原因。
  5. Timely:及時。單元測試應該恰好在使其通過的production code之前撰寫。

Where

單元測試的範圍,以定義來說,單元測試是最小的測試單位,在物件導向中,就是測試一個方法。而方法一定會在某個物件上(即使是靜態方法,也是在型別物件上)。
所以,單元測試通常就只關注在測試的目標物件上,而不管目標物件以外的東西,例如:目標物件所相依的實體物件、相依服務、相依資源、相依環境等等。
單元測試,簡單的說,就是用來「模擬外部如何使用這個目標物件,或是如何與這個目標物件互動」。所以我們所撰寫的單元測試程式,就是模擬與目標物件互動的程式。測試案例,就是該互動下的情境。接著驗證物件的行為是否符合我們預期。
因此,單元測試程式,既然是模擬外部如何使用目標物件,所以也只會針對目標物件對外開放的方法。
而基本上,單元測試透過哪些方式去驗證物件的行為符合預期呢?簡單來說,有三種:
  1. 驗證目標物件的回傳值,如下圖所示:
Dn194488.20F08EF1D94F336317D98F6FD1606C1C(zh-tw,MSDN.10).png
  1. 驗證目標物件的狀態改變,如下圖所示:
Dn194488.95996ACAA870608606D01EB35F8FADC8(zh-tw,MSDN.10).png
  1. 驗證目標物件與外部相依介面的互動方式,如下圖所示:
Dn194488.6532BB93000415AA9B3DE8CC0856E46F(zh-tw,MSDN.10).png

Who

單元測試該由誰來撰寫,就如同前言所說,最應該撰寫的是developer,而非QA/QE。
就如Where段落所說,單元測試簡單的說,是我們在設計物件的時候,預期外部該如何使用這個物件,進而衍生出物件該提供什麼樣的功能、具備什麼樣的行為。正因為物件的設計人、使用人,都是developer,所以單元測試的程式,當然由developer來設計,最為妥當。尤其由使用的角度來寫,最為精準。
歸納幾個基本要點:
  1. 想要達到什麼需求,就是測試案例。而物件的設計,只是為了滿足需求,需求即測試案例。即production code只為了滿足測試程式上的測試案例。
  2. 設計物件的人員,才能知道物件該怎麼給外面使用。
  3. 由外部使用物件的角度來設計測試案例。

When

撰寫單元測試的時機點,簡單分成三個:
  1. 外部需要使用物件,並對其執行結果有所預期時(developing)
  2. Feature的異動時(modifying)
  3. 出現非預期執行結果時(bug fixing)
想清楚,外部的需求是什麼,才能設計出符合需求的物件。
當需求異動時,自然需要針對新的需求,來設計新的測試程式,因為這樣才能驅使目標物件行為的改變。
當出現非預期的執行結果時,通常代表目標物件有著非預期的行為發生,有可能是當初測試案例不足,所以要增加我們的「預期」。
也有可能是當初預期的結果就錯了,那其實就可以當作是第二點,需求的異動。當然對使用端來說,這還是屬於bug,但對物件設計來說,測試案例方向就錯了,當然無法苛求目標物件的行為要對。另外要記住一點:測試程式與production code是一起活著的!

Test Cases的意義

大家買過3C產品或電器吧,基本上拿到一個東西,我們都會先看使用說明書。
大家肯定也寫過一堆「系統分析書」、「程式規格書」、「SA/SD文件」等等...但這些文件,跟最後線上的程式碼,究竟有多少是相同的呢?文件越詳細,代表後面修改的 effort越大。
因為軟體設計,本來就是個需求頻繁變動的過程,往往大家只想「凍結需求」,卻很常因為「凍結需求」搞到作出來的系統難用,因為不符合使用者需求。
我們期望的是:「每一次的需求異動,都是軟體進化的動力,每一次的異動,都是品質的累積,以及更符合使用者的需求。
而文件呢?通常只有一開始分析、設計時寫爽的,因為一旦程式碼寫下去,跟文件搭不搭的起來,只有三個人知道,一個已經離職了,一個是我,另一個我不能說。
鮮少會有文件跟著程式碼一直進行更新的。
但文件卻又是輔助瞭解與說明很重要的東西,那怎麼辦?很簡單,會一直活著的,就只有程式碼。要驗證程式碼是否符合我們預期,最簡單的方式,就是用程式碼驗證它的行為,一翻兩瞪眼,現在的物件究竟滿足了那些功能,哪些情境下可以跑出預期結果,測試案例一目了然。
所以,測試案例的意義與價值是什麼?
  1. 可自動執行、馬上執行、快速執行的物件使用說明書,不會有過期或漏了更新的問題。
  2. 不管什麼情況發生,不管在什麼環境底下,都能確保其執行結果如同預期。
程式碼即文件,高興什麼時候產生文件,就什麼時候產生,保證即時、可運作、童叟無欺。測試案例上面有的,肯定work,而測試案例上面沒有的,不一定會錯,但不打包票。

結論

Working software is based on working test cases

Working software是TDD的整個骨架,也是user最需要的東西。

Test-Driven Development  / 測試驅動開發


為了要做好開發,所以先寫好測試程式,
這對於某些想改變軟體品質的主管或是專案經理來說,
看起來好像是個不小的誘惑,但真的做起來,這那麼簡單嗎?

【摘自: link






參考摘自:

30天快速上手 TDD Day 2 - Unit Testing 簡介

https://msdn.microsoft.com/zh-tw/library/dn194488.aspx


ASP.NET MVC 單元測試系列 (5):瞭解 Stub 假物件


http://blog.miniasp.com/post/2010/09/17/ASPNET-MVC-Unit-Testing-Part-05-Using-Stub-Object.aspx





留言

這個網誌中的熱門文章

何謂淨重(Net Weight)、皮重(Tare Weight)與毛重(Gross Weight)

Architecture(架構) 和 Framework(框架) 有何不同?_軟體設計前的事前規劃的藍圖概念

經得起原始碼資安弱點掃描的程式設計習慣培養(五)_Missing HSTS Header