Upload
terry-chuan-yin-wang
View
885
Download
0
Embed Size (px)
Citation preview
?王泰瑞 , Terry Wang 2015/ November/ 14
Unit test 101
Confidential. Wang Terry, Chuan Yun, all rights reserved.
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• How to write unit test cases?• Unit test tools
• 請大家先下載 scrum poker App
這次不會講到什麼東西?
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• How to write testable code?• Testable code = OO + dependency injection• 閒聊一下 singleton
• 請大家先下載 scrum poker App
這次會講到什麼東西?
Confidential. Wang Terry, Chuan Yun, all rights reserved.
先來聊聊 singleton
Confidential. Wang Terry, Chuan Yun, all rights reserved.
隨堂考
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• Singleton 好不好?singleton
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• Singleton 好不好?• Global 變數是好還是不好?
singleton
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• Singleton 其實是一種 Global 變數• 同意嗎?
singleton
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 幾個 global variable?singleton
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 其實,理論上,可以有無限個• WHY?
singleton
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• singleton 很麻煩的是,當 getInstance() 被呼叫的時候,就生成了一整個真的 instance 出來
• 無法 dependency injection• 所以很難測試• 到底什麼是 dependency injection??• 來估計一下吧!
singleton
Confidential. Wang Terry, Chuan Yun, all rights reserved.
先喘一下
Confidential. Wang Terry, Chuan Yun, all rights reserved.
來拿 Emma 的 code 當例子
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• Emma 下層是 Smack ,處理把”安安您好幾歲住哪“變成 Xmpp 格式的訊息的收送
• Smack 上層有個 XmppSdkAdapterLevel1 的 class
Emma 長怎樣
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 下面接一堆網路的東西 , library 等等 ... 很肥 der
• 我挑了這個 Method:
• 它吃一個 Packet 物件當作參數,回傳 boolean ,或是丟出例外
我想測 XmppSdkAdapterLevel1
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 先丟一個 null 進去看看• paranoid coding?
於是我寫了 unit test code
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• NPE ,但不是傳進去的參數的問題,而是 init 的時候錯了
unit test 跑出來的結果是 ...?
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• init 出 XmppSdkAdapterLevel1 的時候,需要context 物件
我們遇到了什麼問題?
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• init 出 XmppSdkAdapterLevel1 的時候,需要context 物件
• XmppSdkAdapterLevel1 一千多行,你好大,我好怕
我們遇到了什麼問題?
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• init 出 XmppSdkAdapterLevel1 的時候,需要context 物件
• XmppSdkAdapterLevel1 一千多行,你好大,我好怕• XmppSdkAdapterLevel1 是個 singleton
我們遇到了什麼問題?
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• init 出 XmppSdkAdapterLevel1 的時候,需要context 物件
• XmppSdkAdapterLevel1 一千多行,你好大,我好怕• XmppSdkAdapterLevel1是個 singleton
• 這才是最核心的問題
我們遇到了什麼問題?
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• “Tests trump Encapsulation.”• from “The Little Singleton” by Uncle Bob, • clean code, clean coder 的作者
• 原本 singleton 的 instance 長這樣:
• WTF? XmppManager?
XmppSdkAdapterLevel1 是個 singleton
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 我們改成這樣
• 改名+破壞了封裝
XmppSdkAdapterLevel1 是個 singleton
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 來改寫 unit test code 吧!
• 我們用 Mock 物件覆寫了 sInstance• 下一步是… ? 處理紅字 and 想辦法拿到 unit test 的
Context 物件
XmppSdkAdapterLevel1 是個 singleton
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 處理紅字 • 用 inner class
• 想辦法拿到 unit test 的 Context 物件• 用 AndroidTestCase
XmppSdkAdapterLevel1 是個 singleton
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 發現建構子哭哭了XmppSdkAdapterLevel1 是個 singleton
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 發現建構子哭哭了• and 要想辦法把
Context 塞進去啊!
XmppSdkAdapterLevel1 是個 singleton
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 要想辦法把 Context 塞進去啊!• 來寫一個新的建構子:XmppSdkAdapterLevel1 是個 singleton
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 再回來寫 unit test code :XmppSdkAdapterLevel1 是個 singleton
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 終於可以開始測試了 !!XmppSdkAdapterLevel1 是個 singleton
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• test 結果是綠燈XmppSdkAdapterLevel1 是個 singleton
Confidential. Wang Terry, Chuan Yun, all rights reserved.
喘一下想一下
我們解決了什麼問題?
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• init 出 XmppSdkAdapterLevel1 的時候,需要context 物件
• XmppSdkAdapterLevel1 一千多行,你好大,我好怕• XmppSdkAdapterLevel1 是個 singleton
我們解決了什麼問題?
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 透過 AndroidTestCase 拿 Context 物件• 適度的破壞封裝• 使用 Mock 物件• 把 Context 放在 Constructor 的參數傳進去• 這就是 Dependency Injection其中的一招
我們怎麼解決的?
Confidential. Wang Terry, Chuan Yun, all rights reserved.
剛剛傳了一個 Null現在來傳一個 Mock 的 Packet 吧
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 一樣, Mock 出一個 MockPacket class傳假的 Packet 物件
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 寫一個新的 test function傳假的 Packet 物件
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 結果是紅燈
• 因為 sendPacket() 回傳值為 false• 為什麼是 false?? 進去看看囉~
傳假的 Packet 物件
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 好像是還沒 connected• 那怎麼辦?
進來看看 sendPacket()
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 好像是還沒 connected• 那怎麼辦?• 解法 A: 進去看 isConnected() ,並且想辦法把他弄到
變成 true
• 會很麻煩,但看起來,專心搞個三個小時是有機會的
進來看看 sendPacket()
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 好像是還沒 connected• 那怎麼辦?• 解法 B: MockXmppAdapterLevel1 覆寫
isConnected()
進來看看 sendPacket()
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 幹,兩個都爆了再跑一次 unit test
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 先來解 Null 的那個• 一看錯誤訊息,原來是 NPE ,真的是因為傳了 Null
進去造成的
再跑一次 unit test
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 怎麼處理比較好?• 解法 A: 繼續往外拋出例外• 解法 B: 用個 if 判斷, null 的話回傳 false• 解法 C: 默默自己 catch 起來
• 因為這個 method 會開給上層用,我決定解法 A
Null Pointer Exception 是個有深度的問題
Confidential. Wang Terry, Chuan Yun, all rights reserved.
繼續往外拋出例外
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 剩另外一個
• 居然又是 NPE• 沒道理啊,我傳的不是 Null 啊• 進去看看
再跑一次 unit test
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 原來是 mConnection 為 null• 那怎麼辦?
再進來看看 sendPacket
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 一樣用 Dependency Injection 的技巧, Mock 一個connection ,並把它當作新的參數傳進 Mock 的XmppAdapterLevel1 的 constructor
mConnection 為 null
Confidential. Wang Terry, Chuan Yun, all rights reserved.
在 Unit test code 裡 Mock 出一個 XmppConnection 物件
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• MockXmppAdapterLevel1 要塞進 connection 到新的 constructor
把 XmppConnection 放進 XmppSdkAdapterLevel1 的 constructor
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• XmppSdkAdapterLevel1把 XmppConnection 放進 XmppSdkAdapterLevel1 的 constructor
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• unit test code
• 使用 Setup()• Member
variables• Shorter test
methods
整理一下
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 幹,兩個又都爆了
• 進去 XMPPConnection.sendPacket 看看
再跑一次 unit test
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 有兩個東西要注意: isConnected() 跟 packetWriter• 可以繼續使用 dependency injection ,一路做下去 or
…
看看 XMPPConnection 的 sendPacket()
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 可以繼續使用 dependency injection ,一路做下去 or …• 我們可以直接覆寫MockXmppConnection的 sendPacket()• 為甚麼不一直做下去?
其實我們現在在測 XmppSdkAdapterLevel1 的 sendPacket
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 裡面什麼都不做覆寫 MockXmppConnection 的 sendPacket
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 綠燈再跑一次 unit test
Confidential. Wang Terry, Chuan Yun, all rights reserved.
喘一下想一下
我們怎麼解決的?
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 大量使用 Mock 物件• 適度覆寫• 把 Mock 物件放在 Constructor 的參數傳進去• Dependency Injection
我們怎麼解決的?
Confidential. Wang Terry, Chuan Yun, all rights reserved.
下一步呢?
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 寫多一點 unit test case• 比如說,離線狀態呢?• 比如說,其他奇怪的 Packet 呢?正常的 Packet 呢?
• 都可以用 Mock object 來做• 這一個 method 就功德圓滿了~~~阿彌陀佛
下一步呢?
Confidential. Wang Terry, Chuan Yun, all rights reserved.
喘一下想一下
我們到目前為止做了什麼?
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 學會了 Dependency Injection
• 可以不用真正建立起 Xmpp 的連線,而測試 Xmpp 的sendPacket方法的正確性
• 如果照樣照句的話:• 可以不用真正建立起 XXXX連線,而測試 XXXX 的 OOO方法
的正確性
我們到目前為止做了什麼?
Confidential. Wang Terry, Chuan Yun, all rights reserved.
我覺得還蠻酷的
Confidential. Wang Terry, Chuan Yun, all rights reserved.
你覺得呢?
Confidential. Wang Terry, Chuan Yun, all rights reserved.
接下來我們來想想 unit test 的本質
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• unit test 就是測一個 method 的正確性到底 unit test 是什麼
我是一個 method
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• method 或許有 input ,就是參數的意思到底 unit test 是什麼
Input
我是一個 method
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• Method也或許有 output ,就是回傳值的意思到底 unit test 是什麼
Input output
我是一個 method
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 當然,也有可能會拋出例外到底 unit test 是什麼
Input output
Exception
我是一個 method
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 每個 method 裡面都有自己的邏輯到底 unit test 是什麼
我是一個 method
Input output
Exception
邏輯
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 這些邏輯,常常需要呼叫別的 Method ,拿到他的 output後,完成自己的邏輯到底 unit test 是什麼
我是一個 method
Input output
Exception
邏輯別的 method
Confidential. Wang Terry, Chuan Yun, all rights reserved.
我是一個 method
• 我們要測的是橘色的部分,也就是 input與 output ( + exception) 的關係
到底 unit test 是什麼
Input output
Exception
邏輯別的 method
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 灰色的別的 method ,我們不用管他,所以我們需要 mock 的物件來做灰色的部分到底 unit test 是什麼
我是一個 method
Input output
Exception
邏輯
請Mock我
Confidential. Wang Terry, Chuan Yun, all rights reserved.
而這就是 unit test 的本質
Input output
Exception
邏輯
Confidential. Wang Terry, Chuan Yun, all rights reserved.
所以我們需要 Dependency injection來幫忙 unit test
Confidential. Wang Terry, Chuan Yun, all rights reserved.
• 因為“測試贏”,所以我們克服了.• 但是感覺好像開了後門,藉由破壞封裝而破壞了” singleton-ness” ,這其實不好.• 所以,接下來在寫程式的時候,要寫 singleton 又想要做 unit test 的話,要真的想清楚唷~~~
Emma 的程式裡面 已經有太多 singleton 了
Confidential. Wang Terry, Chuan Yun, all rights reserved.
啾咪~
Confidential. Wang Terry, Chuan Yun, all rights reserved.
Confidential. Wang Terry, Chuan Yun, all rights reserved.
我知道大家都很聰明都沒問題
Confidential. Wang Terry, Chuan Yun, all rights reserved.
我來問問題好了
Confidential. Wang Terry, Chuan Yun, all rights reserved.
如果一個 class 的建構式需要很多參數比如說 40 個好了那我不是 Mock 到死 ?
Confidential. Wang Terry, Chuan Yun, all rights reserved.
如果一個 class A 在很深的地方比如說 離 UI 層 40 個 new 的地方而這個 class A 的建構式需要另一個 class B 當參數而前 39 個其他的 class 都沒用到 class B只有第一層有用到所以我從第一層一路傳 B 下去給 A?那我不是 Mock 到死?
Confidential. Wang Terry, Chuan Yun, all rights reserved.
為什麼 Clean code 說函式的參數要越 __越好?
Confidential. Wang Terry, Chuan Yun, all rights reserved.
假設我們有一個 method吃一個 int 參數參數的正常值是 0 ~ 10我們理論上要寫幾個 unit test cases?
Confidential. Wang Terry, Chuan Yun, all rights reserved.
為什麼不測 private methods?如果拰杯偏偏就是要測怎麼辦 ?
Confidential. Wang Terry, Chuan Yun, all rights reserved.
使用 reflection?Why?
Why not?
Confidential. Wang Terry, Chuan Yun, all rights reserved.
你說 singleton 不好可是 Android 的 code 一缸子 singleton比如說 WifiManager, XXXXManager 等等都碼是 singleton 啊~