1. 測(cè)試什么:
1) 測(cè)試輸出結(jié)果,測(cè)試返回結(jié)果的正確性
2) 測(cè)試邊界條件
格式
順序
范圍
外部條件
值的存在,特殊值,null,0
時(shí)間
3) 測(cè)試行為,內(nèi)在邏輯關(guān)系,驗(yàn)證相反的邏輯關(guān)系
4) 相互校驗(yàn),反復(fù)核對(duì),多種方式驗(yàn)證
5) 考慮所有可能出錯(cuò)的條件,模擬錯(cuò)誤情況
6) 性能差,壓力大,吞吐率小,響應(yīng)率低等特殊情形
2. 良好測(cè)試的屬性
1) 自動(dòng)化
2) 全面
3) 可重復(fù)
4) 獨(dú)立
5) 高質(zhì)量,和產(chǎn)品代碼一樣對(duì)待
3. 何時(shí)運(yùn)行測(cè)試
1) 寫了一個(gè)新的方法,功能
2) 修了一個(gè)bug
3) 成功編譯,修改了編譯錯(cuò)后
4) Check in之前
5) 持續(xù)集成
4. Review測(cè)試代碼,
1) 在寫業(yè)務(wù)代碼之前, TDD
2) 寫好業(yè)務(wù)代碼之后,業(yè)務(wù)代碼和測(cè)試代碼要一起review
5. 測(cè)試已有的代碼
1) 代碼中頻繁出現(xiàn)問(wèn)題的部分
2) 新添加的代碼邏輯
6. Design
1) 關(guān)注點(diǎn)分離,使可測(cè)試的邏輯分離出來(lái),也方便寫出獨(dú)立的測(cè)試
2) 弄清楚業(yè)務(wù)邏輯中的規(guī)律性,對(duì)這種規(guī)律進(jìn)行測(cè)試
3) TDD,改善已有的接口
4) 驗(yàn)證輸入,輸出
5) 先寫測(cè)試,TDD,便于寫出可測(cè)試的代碼
6) 設(shè)計(jì)要考慮可測(cè)試性
7) Test程度:也不能過(guò)度test,破壞了class的封裝性,也不能為了test使軟件過(guò)度松散,當(dāng)然也不能濫用mock object,這會(huì)使得test不夠友好,重構(gòu)變難。可以對(duì)一些public的interface進(jìn)行測(cè)試,然后對(duì)類的行為進(jìn)行測(cè)試。
8) 表達(dá)意圖:測(cè)試就是文檔
9) 不要修改SUT (system under test), 如果我們使用了Test-Specific子類,要保證我們沒(méi)有修改需要驗(yàn)證的業(yè)務(wù)邏輯,可能只是修改一些可訪問(wèn)性或者注入一些間接的輸入。
10) 保持測(cè)試的獨(dú)立性
11) 隔離SUT,使SUT和其他軟件或者非測(cè)試部分無(wú)關(guān),可以用依賴注入或者依賴lookup的方式輸入test specific子類。使得測(cè)試不受其他部分影響。
12) 最小化測(cè)試的重疊部分
13) 最小化不可測(cè)試的代碼,比如遇到GUI,多線程,要把可測(cè)試的代碼分離出來(lái)
14) 使測(cè)試邏輯和產(chǎn)品代碼邏輯分開(kāi),盡量不使用test hook
15) 將測(cè)試代碼和產(chǎn)品代碼同等對(duì)待
7. 測(cè)試陷阱
1) 以前的測(cè)試有問(wèn)題沒(méi)關(guān)系(代碼重構(gòu)后),測(cè)試不是產(chǎn)品代碼不重要
2) Smoke test, assertTrue(true),無(wú)assert
3) 只能在某個(gè)或者某些特定的環(huán)境中跑通,比如本地standalone,windows等等
4) 浮點(diǎn)數(shù)問(wèn)題
5) 測(cè)試要跑很長(zhǎng)時(shí)間
6) 測(cè)試?yán)鲜切枰膭?dòng),可能代碼強(qiáng)耦合,需要重構(gòu)
7) 需要經(jīng)常的debug
8) Test有時(shí)跑的通,有時(shí)跑不通,是不是用了隨機(jī)數(shù),產(chǎn)生不符合規(guī)范的測(cè)試數(shù)據(jù), 環(huán)境是否有依賴性?
9) 測(cè)試不夠自動(dòng)化,經(jīng)常需要手動(dòng)干預(yù)
10) 測(cè)試代碼的維護(hù)很困難:測(cè)試代碼寫的不夠?qū)I(yè)等等
11) 測(cè)試的時(shí)候沒(méi)有問(wèn)題,上了產(chǎn)品出現(xiàn)問(wèn)題:測(cè)試是不是很久才跑一次?環(huán)境是不是有依賴?
8. 如何寫一個(gè)測(cè)試
新代碼
TDD
寫一個(gè)失敗的測(cè)試
編寫代碼讓這個(gè)測(cè)試可以跑通
重復(fù)進(jìn)行第一步和第二部
在這個(gè)過(guò)程中,主動(dòng)地進(jìn)行重構(gòu)
當(dāng)沒(méi)有新的測(cè)試可以加入,所有測(cè)試也都跑通了,此時(shí)也就完成了
修改一個(gè)bug
找出bug.先
寫一個(gè)失敗的測(cè)試,標(biāo)識(shí)出bug的存在
修改代碼直至測(cè)試跑通
檢驗(yàn)是否所有其他測(cè)試是否都能跑通,表示沒(méi)有影響其他的代碼邏輯
9. 測(cè)試依賴環(huán)境的setup 和 teardown
Fresh fixture: 每個(gè)test方法執(zhí)行都會(huì)setup和teardown
Shared fixture:可以為多個(gè)test方法,test case使用,一般用于那種創(chuàng)建開(kāi)銷很大容易導(dǎo)致slow test的資源
Minimal fixture
Standard fixture
Setup:
· Test方法里自己構(gòu)建,minimal fixture
· Setup方法:standard fixture
· Test utility,create方法
· Lazy loading,只能用于那些不需要teardown的
· Shared fixture,可以預(yù)先通過(guò)back door的方式準(zhǔn)備好,可以用class static屬性預(yù)先創(chuàng)建,可以用lazy load,可以用junit 4的@before, @after,甚至可以用test chain(個(gè)人認(rèn)為不推薦,test之間存在依賴和交互,不夠獨(dú)立)
Teardown
Fresh fixture, 通常在teardown方法里實(shí)現(xiàn),可以借助于test helper,基類。
如果是某個(gè)test方法特有的,可能需要在該test方法里的finally里實(shí)現(xiàn),也可以借助于test helper。
10 Verification(assertion)
1) State assertion: 驗(yàn)證狀態(tài),輸出。state assertion,
問(wèn)題
· 相同的assertion如果經(jīng)常重復(fù)出現(xiàn)在不同的test方法里?
可以用Expected object或者自定義的assertion來(lái)替換
· 如何增加assert的可讀性?
可以用Expected object或者自定義的assertion來(lái)替換(test specific assertion,比如test 方法中含有條件判斷邏輯,可以將這部分提取為自定義的assertion,從而保持測(cè)試代碼的簡(jiǎn)潔和可讀性。
2) Behavior verification: 驗(yàn)證交互,行為,間接輸出,不僅僅是直接的返回結(jié)果。Mock Object可以驗(yàn)證和SUT之間交互的行為邏輯,或者用spy object判斷過(guò)程的間接結(jié)果
3) Delta assertion:用在有shared fixture時(shí),但是不能解決并發(fā)多個(gè)test同時(shí)執(zhí)行出現(xiàn)的沖突問(wèn)題,可以解決另外一個(gè)test修改了shared fixture,接著跑run這個(gè)test出現(xiàn)錯(cuò)誤的問(wèn)題。就是在test run之前先獲得shared fixture的情況,run之后,驗(yàn)證變化,不驗(yàn)證絕對(duì)值。
4) Guard Assertion:將if 判斷用assertNotNull或者其他assertion語(yǔ)句替換
11. Mock對(duì)象的使用
Mock什么
Mock的是依賴組件不是被測(cè)試的單元
何時(shí)需要Mock
實(shí)際對(duì)象的行為是不可預(yù)測(cè)的
實(shí)際對(duì)象很難準(zhǔn)備,初始化,啟動(dòng)
實(shí)際對(duì)象的行為很難觸發(fā),重現(xiàn)
實(shí)際對(duì)象的行為很慢
實(shí)際對(duì)象需要人工干預(yù),有UI
需要觀察實(shí)際對(duì)象的內(nèi)部行為
存在的實(shí)際對(duì)象是我們無(wú)法控制和觀察的,比如當(dāng)我們需要獲取SUT的間接輸入時(shí)
分類:按功能分
Test Stub
SUT對(duì)實(shí)際對(duì)象的間接輸入有依賴性,test stub使得測(cè)試對(duì)SUT的間接輸入有控制能力。
Test Spy
超越test stub,它還可以捕捉SUT的間接輸出,由test方法來(lái)驗(yàn)證。
Mock Object
超越test spy,它也驗(yàn)證間接輸出,也可以模擬間接輸入,但是它的實(shí)現(xiàn)原理,使用方法和test spy是不同的。
Test stub,test spy,mock object都可以通過(guò)JMock生成,但test stub,test spy還可以通過(guò)hard code方式或者采取用戶可配置的方式
Fake Object
Fake Object以極其簡(jiǎn)單的方式實(shí)現(xiàn)了實(shí)際對(duì)象的所有功能,它只是為test構(gòu)建的。但是我們不會(huì)把fake object作為控制點(diǎn)和觀察點(diǎn)。主要原因是因?yàn)閷?shí)際對(duì)象很難創(chuàng)建,或者太慢等等,并不是為了驗(yàn)證它的間接輸出或者內(nèi)部行為。比如用hashtable fake數(shù)據(jù)庫(kù),內(nèi)存數(shù)據(jù)庫(kù),fake web service,我們可以通過(guò)依賴注入或者lookup的方式將fake object和SUT關(guān)聯(lián)起來(lái),在實(shí)際使用中用實(shí)際對(duì)象替換
Dummy Object
只是為了湊足參數(shù),dummy object什么都不干,但是區(qū)別于null,可能都是空實(shí)現(xiàn),沒(méi)有值。SUT不會(huì)和dummy object有邏輯交互。
12. 如何訪問(wèn)SUT的私有屬性或者測(cè)試它的私有行為
同樣的package名,存放于不同的folder下面
繼承SUT,test specific subclass
反射
把屬性或者行為改成public
13. 如何解決數(shù)據(jù)庫(kù)的依賴問(wèn)題
Database sandbox
一人一個(gè)database,schema更改后,保持database的同步。這其實(shí)并不能完全解決由于環(huán)境問(wèn)題而導(dǎo)致的測(cè)試的不穩(wěn)定性,因?yàn)槟硞€(gè)developer自己的test之間也有可能出現(xiàn)db使用沖突。
每個(gè)測(cè)試人員,開(kāi)發(fā)人員,測(cè)試相關(guān)人員都有一個(gè)本地的數(shù)據(jù)庫(kù),或者在服務(wù)器上都有一個(gè)虛擬機(jī)。使用一些輕量級(jí)的db或者in memory db。
不同的人配備不同的db schema,必須使用同樣的數(shù)據(jù)庫(kù)結(jié)構(gòu)
使用共享的db,但是要保證不同的用戶只能修改自己私有的數(shù)據(jù),不能修改共享的數(shù)據(jù)。
存儲(chǔ)過(guò)程的自動(dòng)化測(cè)試
存儲(chǔ)過(guò)程開(kāi)發(fā)人員用db編程語(yǔ)言編寫單元測(cè)試,并在db級(jí)別對(duì)其進(jìn)行單元測(cè)試,這些測(cè)試是跑在db里的
不需要為寫測(cè)試再多了解一門開(kāi)發(fā)語(yǔ)言
測(cè)試和SUT保存在一個(gè)地方
可以TDD
應(yīng)用開(kāi)發(fā)人員用應(yīng)用編程語(yǔ)言編寫單元測(cè)試,并在應(yīng)用級(jí)別對(duì)其進(jìn)行測(cè)試,把存儲(chǔ)過(guò)程當(dāng)成黑盒進(jìn)行測(cè)試,DbUnit
如何teardown db資源
Truncate table
Transaction rollback