單元測試 vs 集成測試,你該怎麼選?

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在1998年,Kent Beck編寫了sUnit,一個面向SmallTalk的單元測試框架。之後,他將這個框架移植到Java,即jUnit。從那時起,xUnit框架擴展到那些最流行的編程語言。比較新的語言,如Golang和Rust,已經將測試直接合併到編譯器和標準庫中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/2e\/2ea6f42b29f3067adea10c6df993a393.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是單元測試並不是唯一。還有集成測試和性能測試等等。在我看來,集成測試和單元測試是健壯軟件的基石。因此,今天讓我們看看單元測試與集成測試之間的區別,以及你什麼時候該選擇哪種測試。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"什麼是一個單元?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/75\/752da7164ec2eacb17d04442c84df420.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"一個單元是邏輯上分離的最小代碼塊"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"單元測試是一種孤立地測試儘可能小的代碼片段的測試。那麼,什麼是一個單元?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"術語“單元”來自數學。數字1被認爲是單元,因爲它是最小的自然數。它是最小的正整數。以此類推,你源代碼的一個單元就是邏輯上與其餘代碼分離的最小代碼片段。它是一個完整的且邏輯上不同的代碼片段,而且是最小的部分。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在大多數編程語言中,你的單元會是一個函數或方法調用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"單元測試的好處是,如果你的代碼由獨立的小片段組成,那麼,爲它們編寫測試就相當容易。這種易編寫性意味着你可以在開發功能時完成單元測試。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"與其它形式的測試相比,單元測試的執行時間相當短。這意味着你可以頻繁運行單元測試。隨着軟件的成熟,一套單元測試是防止迴歸和降低維護成本的有力工具。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"追溯單元測試"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在考慮將單元測試添加到現有軟件時,需要考慮成本和收益。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"單元測試的一個關鍵假設是,被測試的軟件很容易分成不同的單元。在沒有考慮單元測試編寫的軟件中,這個假設很少成立。向現有軟件添加單元測試通常是一種非常好的方法,來穩定軟件並防止將來回歸,但是重構代碼來支持簡單的單元測試可能需要大量工作,甚至會引入新的缺陷。在考慮將單元測試添加到現有軟件時,需要考慮成本和收益。如果你的代碼正在工作,如果代碼很少需要修改,如果代碼不容易進行單元測試,那麼加入單元測試的好處可能無法保證成本。在這些情況下,可以依靠集成測試來防止該領域的缺陷。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"什麼是集成測試?"}]},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/d9\/d9be70604d99357a75f4fb699cf0c869.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"集成測試聚焦於整個軟件棧"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果單元測試的哲學是基於這樣一種認識,即測試小的獨立代碼片段是防止迴歸的一種好方法,那麼集成測試是基於這樣一種理解,即事情通常在邊緣狀態出錯。外部世界是一個混亂的地方,它與你代碼交互的地方通常是意外發生的地方。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可以通過單元測試實現100%代碼覆蓋率,但仍然發現你的軟件失敗。你可能試圖從錯誤的位置讀取文件,或者你的軟件可能從一個調用的服務得到預期之外的輸出,或者它可能以一種無效的方式調用數據庫。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"儘管單元測試應該快速運行並且數量衆多,但是一個好的集成測試策略應該關注較少數量的高影響測試。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這些測試應該跨越單元測試無法跨越的所有界限,寫入文件系統,接觸外部資源,等等。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"當集成測試棘手時"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"某些外部系統確實很難集成到測試中。這是因爲它們在現實世界中有着無法消除的副作用:金融交易、電子郵件發送、物理移動一個噴漆機器人等。在你在測試中放棄並避開它們之前,找找解決方案。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"許多外部系統有一個文檔化的方法來在集成測試中使用它們。支付處理程序通常有測試信用卡號,可以設置具有測試郵箱賬戶的測試用戶來測試郵件發送。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"集成測試越接近真實世界的交互,就越有可能發現問題並提供真正的價值。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Amazon SES——"},{"type":"link","attrs":{"href":"https:\/\/docs.aws.amazon.com\/ses\/latest\/DeveloperGuide\/send-email-simulator.html","title":"","type":null},"content":[{"type":"text","text":"Test email addresses"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Paypal——"},{"type":"link","attrs":{"href":"https:\/\/developer.paypal.com\/docs\/payflow\/payflow-pro\/payflow-pro-testing\/","title":"","type":null},"content":[{"type":"text","text":"Test credit card numbers"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"UPS——"},{"type":"link","attrs":{"href":"https:\/\/www.ups.com\/us\/en\/help-center\/sri\/developer-instruct.page","title":"","type":null},"content":[{"type":"text","text":"Test api mode"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"一個電子商務例子"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"假設你正在編寫一個簡單的電商網站,一個簡化版的amazon.com。這裏的細節很重要,所以我們假設,你會使用PostgreSQL作爲你的數據存儲,使用PayPal進行支付,使用UPS進行發貨、使用Amazon Simple Email Service來發送電子發票郵件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"單元測試:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"單元測試策略將以一種孤立的方式測試應用程序的邏輯。這可能包括:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"測試稅費計算邏輯是否正確地計算出各個司法管轄區的稅費"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"測試放置到購物車數據結構中的項目是否被正確添加"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"測試折扣代碼是否被正確使用"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這些領域中的每一個都可能有幾個測試。每個測試將驗證一小部分功能。單元測試的能力來自它們的數量、簡單性以及它們的執行速度和便捷性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"集成測試:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另一方面,你的集成測試將專注於測試你的電子商務代碼與其它系統的交互。這意味着不僅要測試與數據存儲的集成,還要測試與郵件發送服務的集成、與支付服務的集成等等。這些可能包括:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"測試是否可以從外部運輸服務中檢索運輸費率"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"測試發票是否可以生成並正確發送"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"測試訂單信息是否可以持久化並從數據存儲中正確檢索"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"測試交易是否可以發送並被支付處理程序正確處理"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這些功能中的每一個都可能需要一個或兩個集成測試來驗證。這些測試運行起來會比較慢,可能涉及一些安裝和拆卸步驟。結果是,每個測試的代碼覆蓋率會相當大。這些測試將通過捕獲單元測試不能捕獲的問題來產生價值。然而,維護成本和執行時間可能會比較高。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"集成測試 vs 單元測試"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/cb\/cbc91858e0f7fd898084bc1977c70974.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"是時候正面比較了"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那麼,應該首選哪種類型的測試呢?單靠兩者中的任一個都是不夠的。這兩者都是綜合測試計劃的一部分。讓我們直接比較一下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/02\/02a33f645ea12f88a33855ce8df2415b.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"基於理想化測試的工作軟件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"每種情況都是獨特的,基於在其它情況下有效的建議不應盲目遵循。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現在我們明白了,單元測試不應該觸及文件系統,而集成測試應該只集成鬆散的組件。但實際上,將測試劃分爲兩個明確的類別有點太簡單了,如果我們只關注定義,我們就會忽略目標,即正確的工作軟件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一些非常有想法的開發者認爲"},{"type":"link","attrs":{"href":"https:\/\/dhh.dk\/2014\/tdd-is-dead-long-live-testing.html","title":"","type":null},"content":[{"type":"text","text":"單元測試可以並且應該讀寫數據庫"}]},{"type":"text","text":"。其它人則認爲單元測試是一種浪費,粗粒度的集成測試提供的價值最大。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"問題是,每種情況都是獨特的,基於在其它情況下有效的建議不應盲目遵循。需要牢記的一個問題是,這個測試要捕獲什麼類型的缺陷。如果每個測試都是經過深思熟慮編寫來提升軟件可靠性的,如果測試在不再有價值時被刪除,那麼隨着時間的推移,將發現爲特定項目提供最大價值的特定測試方法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原文鏈接:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/blog.earthly.dev\/unit-vs-integration\/","title":null,"type":null},"content":[{"type":"text","text":"https:\/\/blog.earthly.dev\/unit-vs-integration\/"}]}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章