(2023.01.31 Tues)
Unit test related
unit test和integration test的區(qū)別
(2023.02.02 Thur)
unit test和integration test的區(qū)別在于隔離程度(level of isolation)亭罪。Unit test必須完全隔離,而integration test在執(zhí)行過程中不能擺脫對(duì)外部依賴理朋。Integration test通常以整合的方式測(cè)試多個(gè)模塊厚掷,因此有了integration test這個(gè)名字。Integration test比unit test提供英勇的更多高級(jí)視角爬凑,因此其提供的反饋更真實(shí)而較少聚焦在某一單一功能徙缴。因?yàn)閕ntegration test對(duì)外部的依賴,可能導(dǎo)致效率變低并難于設(shè)置(setup)嘁信。
Best Practices
(2023.01.31 Tues)
每次只測(cè)一個(gè)功能 Test one thing at a time
比如測(cè)試一個(gè)函數(shù)于样,該函數(shù)返回兩個(gè)數(shù)的和。顯然可以調(diào)用任意兩個(gè)數(shù)字的組合并檢驗(yàn)結(jié)果是否正確來(lái)確認(rèn)函數(shù)是否正確潘靖,盡管這個(gè)方法有若干問題穿剖。
更好的方式是對(duì)函數(shù)的不同部分使用獨(dú)立的測(cè)試。比如可以檢測(cè)該函數(shù)能否正確計(jì)算兩個(gè)正數(shù)的和卦溢,另一個(gè)測(cè)試檢測(cè)能否計(jì)算兩個(gè)負(fù)數(shù)的和糊余,等等秀又。這種獨(dú)立的測(cè)試,其優(yōu)勢(shì)在于贬芥,1) 易于設(shè)計(jì)和維護(hù)吐辙,每次只需要關(guān)注一件事,2) 更精確蘸劈,容錯(cuò)的機(jī)會(huì)少昏苏,3) 易于debug,如果test失敗威沫,很容易知道哪個(gè)部分出錯(cuò)了贤惯。
總之,每次測(cè)試一個(gè)功能會(huì)給unit test帶來(lái)更高效率棒掠,更快更準(zhǔn)確救巷,更易于debug。
起個(gè)有意義的名字 Name tests meanfully
比如測(cè)試頁(yè)面的login功能句柠,用一個(gè)test_login
方法來(lái)執(zhí)行浦译。一旦test fails,則無(wú)法知道是因?yàn)槭裁炊鴏ogin失敗溯职。但如果將test_login功能拆分為多個(gè)不同的模塊精盅,比如test_login_with_incorrect_username
,test_login_with_incorrect_password
谜酒,等等方法叹俏,則不同的測(cè)試方法失敗可以帶給QA精確的信息,關(guān)于哪里出現(xiàn)了錯(cuò)誤僻族。
除此之外粘驰,有意義的名字對(duì)于非開發(fā)者來(lái)說也友好,見名知其含義述么。
保證單元測(cè)試簡(jiǎn)短而精煉 Keep unit test short and focused
如果unit test過長(zhǎng)蝌数,則很可能測(cè)試超過一件事,將導(dǎo)致測(cè)試難于理解和維護(hù)度秘,同時(shí)也增加測(cè)試過程出問題的機(jī)會(huì)。
如果unit test過于簡(jiǎn)短精煉唆貌,則不會(huì)覆蓋特別多的內(nèi)容,可能會(huì)導(dǎo)致不同的test suite之間有疏漏粱侣。
最好的方案是在簡(jiǎn)短和精煉之間取得一個(gè)平衡油猫,使得測(cè)試易于理解維護(hù)睬关,同時(shí)提供足夠的覆蓋(adequate coverage)电爹。
用斷言驗(yàn)證結(jié)果 Use assertions to validate results
Python unit test中的assertion是一個(gè)基礎(chǔ)部分恤煞,用于判斷代碼中某個(gè)特定不是本該返回怎樣的結(jié)果概漱。如果斷言失敗,則代碼中有bug照弥。
盡管不用斷言也可以通過其他方式判斷代碼是否運(yùn)行正確敢会,但assertion命令更加專業(yè)塞俱。
為每個(gè)測(cè)試方法寫文檔 Write docstrings for each test method
測(cè)試方法中的文檔用于解釋該unit test的目的以及如何使用罐旗。失去了文檔則使用者不得不猜測(cè)該unit test的功能粘我,可能會(huì)導(dǎo)致一些意想不到的錯(cuò)誤都弹。
寫文檔時(shí)迫使代碼開發(fā)者不得不思考每個(gè)test case的最終目的氮昧,幫助理清思路。當(dāng)需要修改該方法梧乘,文檔將提醒開發(fā)該測(cè)試方法的意義和目的仁堪,幫助理順?biāo)悸泛头椒ā?/p>
避免重復(fù) Don't Repeat Yourself (DRY)
代碼重復(fù)不僅低效氛什,也可能導(dǎo)致錯(cuò)誤(error-prone)。如果你需要更新不同地方/方法中的相同代碼段枪眉,則很可能在過程中犯錯(cuò)捺檬。
避免重復(fù)代碼,可以使測(cè)試模塊化贸铜,每個(gè)test case覆蓋單獨(dú)的一個(gè)功能堡纬,使得代碼更高效聂受、易于維護(hù)和少錯(cuò)誤。
(Python)用setUp
和tearDown
創(chuàng)建和清理fixture Make use setUp
and tearDown
to create/clean up Fixture
Fixtures是用于作為測(cè)試基準(zhǔn)(baseline)的對(duì)象或數(shù)據(jù)烤镐。通過setUp()
方法創(chuàng)建fixtures蛋济,可確保每個(gè)測(cè)試以clean slate形式運(yùn)行。這可以避免因使用前一次測(cè)試數(shù)據(jù)導(dǎo)致的假陽(yáng)性(false positives)
用這兩種方法創(chuàng)建和清理fixture同樣可以保證測(cè)試的DRY炮叶。如果在多個(gè)測(cè)試中創(chuàng)建相同的對(duì)象或數(shù)據(jù)碗旅,則fixture是個(gè)理想的解決方案。通過將這些對(duì)象/數(shù)據(jù)抽象成一個(gè)fixture悴灵,可以在多個(gè)測(cè)試中直接引用,而不用重復(fù)代碼骂蓖。
使用fixtures可以提高測(cè)試的可讀性和可理解性积瞒。直接通過名字引用fixtures,而無(wú)需寫一堆代碼用來(lái)創(chuàng)建對(duì)象或數(shù)據(jù)登下,這使得測(cè)試使用的數(shù)據(jù)更加明確茫孔,也易于修改。
測(cè)試結(jié)束時(shí)被芳,如果之前設(shè)置了fixture缰贝,則需要用tearDown()
方法清理資源,否則后續(xù)測(cè)試中隨著未清理的資源漸漲畔濒,測(cè)試將變慢剩晴。setUp()
和tearDown()
應(yīng)成對(duì)出現(xiàn),否則無(wú)法保證每次測(cè)試始于一個(gè)clean slate.
避免不穩(wěn)定測(cè)試 Avoid brittle tests
不穩(wěn)定測(cè)試(brittle test)是隨著被代碼修改而崩潰的測(cè)試代碼侵状。如果測(cè)試過于針對(duì)代碼赞弥,較容易發(fā)生這種情況。
比如要測(cè)試一個(gè)求和函數(shù)趣兄,一個(gè)不穩(wěn)定測(cè)試會(huì)檢測(cè)返回值是否等于特定輸入列表的預(yù)期和绽左。最初這沒什么問題,但是如果函數(shù)內(nèi)部做出改變艇潭,比如用了不同的算法計(jì)算和拼窥,則測(cè)試將確定失效。
更好的方法是寫測(cè)試檢測(cè)函數(shù)對(duì)于一系列不同的輸入返回正確的和蹋凝。如果函數(shù)改變鲁纠,則這個(gè)測(cè)試不容易崩潰。
總之鳍寂,測(cè)試代碼應(yīng)盡可能獨(dú)立于被測(cè)試代碼的具體實(shí)現(xiàn)房交。這樣能保證即便被測(cè)試代碼發(fā)生改變,測(cè)試仍然可以正常運(yùn)行伐割。
經(jīng)常運(yùn)行測(cè)試代碼 Run your test suite regularly
如果被測(cè)試的代碼經(jīng)常修改候味,而測(cè)試代碼不常運(yùn)行刃唤,則很可能測(cè)試代碼逐漸變得低效。
經(jīng)常運(yùn)行測(cè)試代碼可在易于發(fā)現(xiàn)bugs白群。越早發(fā)現(xiàn)越容易解決尚胞。如果被測(cè)試代碼已經(jīng)上線卻未運(yùn)行測(cè)試,則不易追蹤代碼中的問題帜慢。
(2023.02.01 Wed)
測(cè)試應(yīng)保證確定性 Test should be deterministic
確定性指的是在被測(cè)試代碼沒有修改的情況下笼裳,測(cè)試代碼的行為和結(jié)果應(yīng)該總是相同。確定性一定程度上構(gòu)成了測(cè)試的可被信任的程度粱玲。
為保證確定性躬柬,代碼需要完全隔離(completely isolated),測(cè)試不能代碼不能依賴
- 其他test cases
- 環(huán)境變量抽减,諸如當(dāng)前時(shí)間允青、系統(tǒng)語(yǔ)言設(shè)定、文件系統(tǒng)卵沉、網(wǎng)絡(luò)颠锉、API等
總之,test case的輸出產(chǎn)生變化史汗,當(dāng)且僅當(dāng)被測(cè)試的代碼發(fā)生變化琼掠。
測(cè)試成為構(gòu)建過程的一部分 Make test a part of build process
考慮到我們討論的是自動(dòng)化測(cè)試,將測(cè)試加入build process就很有意義停撞。確保構(gòu)建過程執(zhí)行unit test并在測(cè)試失敗時(shí)將構(gòu)建標(biāo)記為broken.
盡管對(duì)于開發(fā)者來(lái)說頻繁在開發(fā)機(jī)上運(yùn)行測(cè)試代碼是必要步驟瓷蛙,特別是基于測(cè)試的開發(fā)(TDD),但這仍然不夠戈毒。在構(gòu)建過程中加入unit test使得系統(tǒng)更加安全速挑,比如開發(fā)者在local開發(fā)時(shí)忘記執(zhí)行unit test,CI server不會(huì)忘記副硅。
Reference
1 climbtheladder點(diǎn)com姥宝,10 Python Unit Testing Best Practices
2 testim點(diǎn)io,Unit Testing Best Practices: 9 to Ensure You Do It Right