[前情提要]
其實半年前我就想報名這堂課,不過可惜最後因為幾個負面思考而打消了念頭,當時在心中埋下了一顆悔恨的種子,告訴自己說,下次再有這個機會請不要錯過。
終於,今年盼到了第三梯的課程,雖然價格提高了4成 =.= ,不過還是自己能夠負擔的範圍啦,當然就報囉。
剛好今年也給了自己一些明確的目標:
1.
2015Q4 要在公司教授Unit Testing的課程:
為了要教別人,所以先來觀摩大神91哥是怎麼教學員的XD
果然Lab中的幾個例子都很務實,也給了我一些方向。
2.
替Team導入完整的Continuous Delivery Solution:
為了使CD更完整,Unit Testing就不能缺席,我一定會建置出一個方便的Solution讓同事們能夠無痛升級。於是呢,獲得老婆的諒解之後,踏上了學習之路!
[我們的痛?]
91在課程的一開始,先分組讓大家自我介紹,請大家分享一下為什麼要來上這堂課。我覺得這是個很好的開始,要解決任何問題之前,請先停下來思考:問題是什麼?釐清問題之後,大家的學習會更努力更有效率。
歸納當天學員們的問題,不外乎就是:
1.
想了解Unit Testing &
TDD
2.
實務上怎麼導入?
以我自己為例,我為什麼想要學這堂課的原因是:
1.
我要導入Unit Testing至我們Team,希望能學到實務的例子,提供簡單的架構,降低同事們的學習門檻。
2.
我要教這堂Unit Testing,所以來學習91是如何教學員。
實務上開發常見的問題有那些呢?我想身為一個Developer,應該對這些問題都耳熟能詳,但是卻無法有效解決:
- 測試環境無法準備
- 不曉得Developer交付的code是否正確?
- Side Effects
- 跟其他Team一起平行開發中,過程中必須相互等待。
其實我們就能透過Unit Test解決上述問題。
1.
測試環境無法準備????
有些系統是屬於外部系統,他們的整合日期可能不是我們這個Team這個層級就能決定的,但是開發不能等他們好再來測,此時我們可以使用Mock
Framework來協助我們克服這個問題。
2.
不曉得Developer交付的code是否正確?
交付code即附上Unit Test。結果清楚明瞭。未來若整合進CD,我們可以在建置後就能知道code是否正確,比起問題要到QA才發現還早多了。Bug是需要提早發現及早治療的。
3.
Side Effects
儘管有Code Review。大部分最難防的還是Side
Effects吧!而完整的Unit Test就能幫大家補上這一塊,改完code隨時執行Unit Test。有沒有改東壞西馬上就知道結果。
遙想當初我想要Refactor某個Feature,卻因為沒有Unit
Test,也沒有QA可以驗證而放棄。如果有Unit Test就會增加我們Developer改code的自信心吧!
4.
跟其他Team一起平行開發中,過程中必須相互等待。
其實這問題跟第一點很像,但是更有可能以design的方式就能解決。體會到Unit Test的魅力之後,自然而然會想寫出可以測試的程式(Testability),在寫code時自然就會考量到Isolated關注點的問題,盡量將Dependency隔離開。最後的實務上就能以Mock Framework輕鬆隔離相依性,只測該測的地方,讓Unit
Test變得簡單,簡單才會有人想用。
這條路上一定會被問到:
如果寫Unit Test,在開發上會增加多少時間?
這讓我想到之前讀”The Art of Unit Testing with Examples in .NET”這本書時,作者以他本身在大型企業的案例作為例子:
Developer開發的時間雖然變成2倍,但是整體的時間是下降的,更重要的是,Bug的數量很顯著的下降。這就是Unit Test的威力。
還有一句話讓我很有感:
面對未知的問題,不要用猜的,用測的最準!!!
[基礎概念]
什麼是Unit Test
幾點定義:
1.
最小的測試單位
2.
外部相依性為零
3.
沒有商業邏輯
4.
Test Case彼此的相依性為零
5.
一個Test Case一次只測一件事
其中最重要的兩點就是 2 & 5
只要把握這些特性,就能寫出有效的Unit Test
如果Test Case測了兩件事,代表測試失敗時會有兩種可能!
3A原則 : Arrange, Act, and
Assert
這裡學到幾個tips:
這裡學到幾個tips:
1.
面對Unit Testing的初學者,我們在導入時可以將Arrange, Act, and Assert的結構用註解隔開。
2.
Naming時也請習慣使用target, expected, and actual
3.
Assert的位置是(expected, actual) 不要搞錯位置囉!
以上作法能夠幫助Developer清楚原則與用法。
Unit Test的特性:FIRST
Fast, Independent, Repeatable, Self-Validating
and Timely
(心得) Repeatable 用Idempotent來解釋會更有說服力
Idempotent : 無論執行幾次,結果都是相同的。
[AreEqual v.s. AreSame]
原則:(MsTest, 但我想NUnit應該也差不多)
1.
比較value type的值用AreEqual()
2.
比較reference type的memory位置是否相同用AreSame()
3.
比較 Collection 可用CollectionAssert
4.
想測是否會有Exception請用[ExpectedException],切記不要自己寫try catch。
測Exception一定要測如果是Scenario1就預期有Exception的Case。
5.
.Net String Object的特性:String雖是Reference Type,但是有實作StringPool的緣故,所以如果是同一個值將會指到同一塊Memory,所以也擁有value type的特性。
[如何解決相依性]
接下來就進入實務上最常碰到的問題了,也開始進入導入上所面臨的困難。
Solution :
- 單一職責
- 依賴Interface
- 依賴注入
- 利用Mock Framework
1.
單一職責
在Design上就盡量讓一個Class,一個Method負責單一的職責。從這裡盡量降低一些Dependency。
2.
依賴Interface
我們要從使用端的角度去定義Interface,從相依的Class中抽取出能共同使用的Interface。
3.
依賴注入
這邊要想個法子讓測試程式可以替換掉有相依性的production code instance。
91介紹了 (1) Constructor Injection & (2)
Parameter Injection
我記得之前書上還有提到(3) Factory Method 以及(4)Extract and
Override
下次再補上
4.
利用Mock Framework
自己去寫上述測試程式是很累人的,也會降低Developer想寫Unit Test的意願,此時Mock
Framework就能幫助大家很簡單地寫出可避開相依性的測試程式。
這次使用的NSubstitute真的很好用,也不像Rhino違反了3A原則。我們可以透過Substitute.For<T>產生stub, 再透過xxx.TestMethod1.Return(R) 取得stub預期會回傳的R (Stub 關心的是回傳值)
若是想要測Mock (與外部的互動) 也能使用xxx.Received().ExpectedMethod()
知道預期是否有呼叫ExpectedMethod() 或是 用DidNotReceived()驗證不被呼叫的情況。(切記,一次只測一件事,如果要測Mock,只要驗證是否有被呼叫即可。)
91的經驗:大部分都是測Stub, Mock的比例大約10%
[非Public的Method該不該測]
不用針對它特別測!!!
但是得透過Public Method測到它
不要為了測試而測試,想想需求吧。非Public的Method一定是在哪裡被Public的Method使用到。
Code Coverage的建議
1.
檢視Test Case是否包含最主要的情境
2.
檢視Test Case是否包含曾經出錯的情境
3.
檢視是否有不必要的code或是缺少對應的Test Case
4.
相對比例盡量只能往上,不要往下
[Internal該如何測試]
在實務上我們有些Class是設定成Internal的,測試程式可能無法取得。
Solution :
1.
AssemblyInfo.cs 加上 [assembly:InternalsVisibleTo(“XXX.Test”)]
2.
AssemblyInfo.cs 加上 [assembly:InternalsVisibleTo(“DynamicProxyGenAssembly2”)]
要給DynamicProxyGenAssembly2看得見才能動態建立stub/mock等
[其他心得]
1. TDD導入與推廣的原則
(i) 如何讓Member不要太過麻煩就能做
(ii)如何盡量不影響現有production code
2. 不要為了測試而去切Interface
要反過來問需求,我們是為了彈性才去切Interface
3. .NET Framework的Object不建議用Interface去抽換
對這堂課有興趣的朋友們
可以關注一下SkillTree什麼時候要推出第四梯的課程喔
自動測試與 TDD 實務開發(使用C#) 第三梯
系列文章
[心得文] 自動測試與TDD實務開發 Day2
0 意見:
張貼留言