JavaRush /Java Blog /Random-TW /什麼是TDD和單元測試[翻譯]
Dr-John Zoidberg
等級 41
Марс

什麼是TDD和單元測試[翻譯]

在 Random-TW 群組發布
本文改編自《完整軟體職業指南》一書的一章。它的作者 John Sonmez 撰寫了該書,並在他的網站上發布了一些章節。
什麼是 TDD 和單元測試 [翻譯] - 1

適合初學者的簡短詞彙表

單元測試或單元測試是編程中的一個過程,可讓您檢查程式原始碼的各個模組的正確性。這個想法是為每個重要的函數或方法編寫測試。 回歸測試是所有類型軟體測試的總稱,旨在檢測原始程式碼已測試區域中的錯誤。此類錯誤(當對程式進行更改後,應該繼續工作的某些內容停止工作時)稱為回歸錯誤。 紅色結果,失敗- 測試失敗。預期結果與實際結果之間的差異。 綠色結果,通過- 陽性測試結果。實際結果與得到的結果沒有什麼不同。***
什麼是 TDD 和單元測試 [翻譯] - 2
我對測試驅動開發(TDD)和單元測試的關係非常複雜,從愛到恨,然後又回來。我是一個狂熱的粉絲,同時對這個和其他「最佳實踐」的使用持懷疑態度。我之所以持這種態度,是因為軟體開發過程中出現了一個嚴重的問題:開發人員,有時甚至是管理者,使用某些工具和方法只是因為它們屬於「最佳實踐」。使用它們的真正原因仍不清楚。有一天,我開始從事某個項目,在此過程中我被告知我們將修改大量單元測試所涵蓋的程式碼。不是開玩笑,大約有 3000 個。這通常是一個好兆頭,表明開發人員正在使用先進的方法。採用這種方法的程式碼通常是結構化的,並且基於深思熟慮的架構。總之,測試的存在讓我很高興,因為這意味著讓我作為程式設計師導師的工作變得更輕鬆。由於我們已經進行了單元測試,我所要做的就是聯繫開發團隊來支援他們並開始編寫我們自己的程式碼。我打開IDE(整合開發環境)並載入專案。
什麼是 TDD 和單元測試 [翻譯] - 3
這是一個大工程!我找到了一個標有“單元測試”的資料夾。「太棒了,」我想。- 讓我們啟動它,看看會發生什麼。只花了幾分鐘,令我驚訝的是,所有測試都通過了,一切都是綠色的(“綠色”是測試的積極結果。表示代碼按預期工作。紅色表示“失敗”或失敗,然後在某有些情況下,代碼無法正常工作- 譯者註)。他們都通過了測試。那一刻,我內心的懷疑醒了。為什麼三千個單元測試,他們一次全部進行,並給出了積極的結果?在我長期的實踐中,我不記得有一次我開始從事一個專案時,程式碼中沒有一個負面的單元測試。怎麼辦?手動檢查!ChY 選擇了一項隨機測試,雖然不是最具啟發性的測試,但很快就很清楚他在檢查什麼。但在研究過程中,我注意到一些荒謬的事情:測驗不包含與預期結果(斷言)的任何比較!也就是說,實際上根本沒有檢查任何內容!測試中有某些步驟,它們被執行了,但是在測試結束時,他應該比較實際結果和預期結果,但沒有檢查。“測試”沒有測試任何東西。我打開了另一個測試。更好的是:帶有結果的比較運算子已被註解掉。太棒了!這是進行測試通過的好方法,只需註解掉導致測試失敗的程式碼即可。我檢查了另一個測試,然後又檢查了一個……他們都沒有檢查任何東西。三千次測試,全部毫無用處。編寫單元測試與理解單元測試和測試驅動開發(TDD)之間存在巨大差異。

什麼是單元測試?

什麼是 TDD 和單元測試 [翻譯] - 4
單元測試的基本思想是編寫測試程式碼的最小「單元」的測試。單元測試通常使用與應用程式原始碼相同的程式語言編寫。它們是直接創建來測試此程式碼的。也就是說,單元測試是檢查其他程式碼正確性的程式碼。我在上下文中相當隨意地使用“測試”這個詞,因為單元測試在某種意義上不是測試。他們沒有經歷任何事情。我的意思是,當您執行單元測試時,您通常不會發現某些程式碼不起作用。您在編寫測試時會發現這一點,因為您將更改程式碼直到測試變綠。是的,程式碼稍後可能會更改,然後您的測試可能會失敗。所以從這個意義上來說,單元測試就是回歸測試。單元測試與普通測試不同,在普通測試中,您需要遵循幾個步驟,然後查看軟體是否正常運作。在編寫單元測試的過程中,您會發現程式碼是否做了它應該做的事情,並且您將更改程式碼直到測試通過。
什麼是 TDD 和單元測試 [翻譯] - 5
為什麼不寫一個單元測試並檢查它是否通過?如果你這樣想的話,那麼單元測試就會變成對某些程式碼模組在非常低的等級上的某種絕對要求。您可以將單元測試視為絕對規格。單元測試確定在這些條件下,使用這組特定的輸入,您應該從該程式碼單元獲得輸出。真正的單元測試識別最小的連貫代碼單元,在大多數程式語言(至少是物件導向的語言)中,它是一個類別。

有時所謂的單元測試是什麼?

什麼是 TDD 和單元測試 [翻譯] - 6
單元測試經常與整合測試混淆。一些“單元測試”測試多個類別或測試大的程式碼單元。許多開發人員聲稱他們編寫了單元測試,但實際上他們編寫的是低階白盒測試。不要和這些人爭論。只要知道他們實際上編寫了整合測試,而真正的單元測試則測試與其他部分隔離的最小程式碼單元。另一件通常稱為單元測試的事情是不檢查預期值的單元測試。換句話說,單元測試實際上並不會測試任何東西。任何測試,無論是否統一,都必須包括某種驗證 - 我們稱之為根據預期結果檢查實際結果。正是這種協調決定了測試是通過還是失敗。總是通過的測試是沒有用的。總是失敗的測試是沒有用的。

單元測試的價值

為什麼我是單元測試愛好者?為什麼稱之為廣義測試是有害的,廣義測試涉及的不是測試與其他程式碼隔離的最小區塊,而是測試更大的程式碼段,即「單元測試」?如果我的某些測試未將收到的結果與預期結果進行比較,會出現什麼問題?至少他們執行程式碼。我會嘗試解釋一下。
什麼是 TDD 和單元測試 [翻譯] - 7
執行單元測試有兩個主要原因。 首先是改進程式碼設計。 還記得我說過單元測試不是真正的測試嗎?當您編寫正確的單元測試時,您會強迫自己隔離最小的程式碼單元。這些嘗試將引導您發現程式碼本身結構中的問題。您可能會發現隔離測試類別並且不包含其依賴項非常困難,這可能會讓您意識到您的程式碼耦合得太緊密。您可能會發現您嘗試測試的核心功能跨越多個模組,導致您認為您的程式碼不夠連貫。當您坐下來編寫單元測試時,您可能會突然發現(相信我,這種情況確實發生了!)您不知道程式碼應該做什麼。因此,您將無法為其編寫單元測試。當然,您可能會在程式碼的實作中發現真正的錯誤,因為單元測試迫使您跳出框框思考並測試您可能沒有考慮過的不同輸入集。
什麼是 TDD 和單元測試 [翻譯] - 8
如果您在建立單元測試時嚴格遵守「獨立於其他程式碼單元測試最小的程式碼單元」的規則,那麼您一定會發現該程式碼以及這些模組的設計有各種各樣的問題。在軟體開發生命週期中,單元測試更多的是一種評估活動而不是測試活動。 單元測試的第二個主要目標是創建一組自動化的回歸測試,可以充當軟體行為的低階規格。這是什麼意思?當你揉麵團時,你不會把它弄碎。從這個角度來看,單元測試就是測試,更具體地說是回歸測試。然而,單元測試的目的並不是簡單地建立回歸測試。在實踐中,單元測試很少捕獲回歸,因為對正在測試的程式碼單元的變更幾乎總是包含單元測試本身的變更。當程式碼作為「黑盒」進行測試時,回歸測試在更高層級上更加有效,因為在這個層級上,程式碼的內部結構可以更改,而外部行為預計保持不變。單元測試依序檢查內部結構,以便當結構發生變化時,單元測試不會失敗。它們變得無法使用,現在需要更改、丟棄或重寫。您現在比許多經驗豐富的軟體開發人員更了解單元測試的真正目的。

什麼是測試驅動開發(TDD)?

什麼是 TDD 和單元測試 [翻譯] - 9
在軟體開發過程中,好的規範是非常有價值的。TDD 方法是,在編寫任何程式碼之前,先編寫一個測試作為規範,即定義程式碼應該做什麼。這是一個極其強大的軟體開發概念,但它經常被誤用。通常,測試驅動開發意味著使用單元測試來指導應用程式程式碼的建立。但事實上,這種方法可以應用於任何層級。但是,在本文中,我們將假設我們正在對應用程式使用單元測試。TDD 方法徹底顛覆了一切,您不是先編寫程式碼,然後編寫單元測試來測試程式碼,而是先編寫單元測試,然後編寫程式碼以使該測試變得綠色。透過這種方式,單元測試“驅動”程式碼開發。這個過程一遍又一遍地重複。您編寫另一個測試來定義程式碼應該執行的更多功能。然後,您編寫並修改程式碼以確保測試成功完成。一旦獲得綠色結果,就開始重構程式碼,即重構或清理它以使其更加簡潔。這個過程鏈通常被稱為“紅-綠-重構”,因為首先單元測試失敗(紅色),然後編寫程式碼以適應測試,確保其成功(綠色),最後優化程式碼(重構)。

TDD 的目標是什麼?

什麼是 TDD 和單元測試 [翻譯] - 10
測試驅動開發(TDD)與單元測試一樣,也可能被錯誤地使用。人們很容易將您所做的事情稱為“TDD”,甚至在不理解為什麼這樣做的情況下遵循實踐。TDD 的最大價值在於執行測試以產生品質規格。 TDD本質上是編寫精確規範的實踐,這些規範可以在編寫程式碼之前自動檢查。測試是最好的規範,因為它們不會說謊。經過兩週的折磨後,他們不會告訴你「這根本不是我的意思」。如果編寫正確,測試要么通過,要么失敗。測試清楚地表明在某些情況下應該發生什麼。因此,TDD 的目標是讓我們在開始實施之前全面了解需要實施的內容。如果您剛開始使用 TDD 並且無法弄清楚測試應該測試什麼,那麼您需要提出更多問題。TDD 的另一個重要作用是保存和優化程式碼。程式碼維護成本高昂。我經常開玩笑說最好的程式設計師是編寫解決問題的最短程式碼的人。或者甚至證明這個問題不需要解決,從而完全刪除程式碼,因為正是這位程式設計師找到了減少錯誤數量並降低維護應用程式成本的正確方法。透過使用 TDD,您可以絕對確定您沒有編寫任何不必要的程式碼,因為您編寫的程式碼只是為了通過測試。有一個稱為 YAGNI 的軟體開發原則(你不會需要它)。TDD 阻止了 YAGNI。

典型的測試驅動開發 (TDD) 工作流程

什麼是 TDD 和單元測試 [翻譯] - 11
從純粹的學術角度來理解 TDD 的意思是困難的。讓我們來看一個 TDD 會話的範例。想像一下,坐在辦公桌前,快速勾勒出您認為的一項功能的高級設計,該功能允許用戶登入應用程式並在忘記密碼時更改密碼。您決定從登入函數的第一個實作開始,建立一個類別來處理登入過程的所有邏輯。您打開您最喜歡的編輯器並建立一個名為「空登入阻止使用者登入」的單元測試。您編寫單元測試程式碼,首先建立 Login 類別的實例(您尚未建立)。然後,您編寫程式碼來呼叫 Login 類別中傳遞空用戶名和密碼的方法。最後,您根據預期結果編寫檢查,檢查具有空登入的使用者實際上是否未登入。您正在嘗試執行測試,但它甚至無法編譯,因為您沒有 Login 類別。您可以修復這種情況,並建立一個 Login 類,並在該類別中建立一個用於登入的方法,以及另一個用於檢查使用者狀態以查看他們是否已登入的方法。到目前為止你還沒有實作這個類別的功能和我們需要的方法。此時您執行測試。現在它可以編譯,但立即失敗。
什麼是 TDD 和單元測試 [翻譯] - 12
現在您返回程式碼並實現功能以通過測試。在我們的例子中,這意味著我們應該得到結果:“用戶未登入。” 您再次運行測試,現在它通過了。讓我們繼續下一個測試。現在讓我們假設您需要編寫一個名為「如果使用者輸入了有效的使用者名稱和密碼,則使用者已登入」的測試。您編寫一個單元測試來實例化 Login 類別並嘗試使用使用者名稱和密碼登入。在單元測試中,您編寫一條語句,要求 Login 類別應對使用者是否登入的問題回答「是」。您執行這個新測試,當然它會失敗,因為您的 Login 類別始終會傳回使用者未登入的資訊。您返回 Login 類別並實作一些程式碼來驗證使用者是否已登入。在這種情況下,您必須弄清楚如何隔離該模組。目前,最簡單的方法是對您在測試中使用的使用者名稱和密碼進行硬編碼,如果它們匹配,則返回結果「使用者已登入」。您進行此更改,執行兩個測試,它們都通過了。讓我們繼續最後一步:查看生成的程式碼,並尋找重新組織和簡化它的方法。所以TDD演算法是:
  1. 創建了一個測試。
  2. 我們為此測試編寫了程式碼。
  3. 重構了程式碼。

結論

什麼是 TDD 和單元測試 [翻譯] - 13
這就是我現階段想告訴您的有關單元測試和 TDD 的全部內容。事實上,嘗試隔離程式碼模組會遇到很多困難,因為程式碼可能非常複雜且令人困惑。很少有類別是完全孤立存在的。相反,它們具有依賴關係,而這些依賴關係又具有依賴關係,等等。為了處理這種情況,TDD 老手使用模擬,它透過替換依賴模組中的物件來幫助隔離各個類別。本文只是對單元測試和 TDD 的概述和稍微簡化的介紹,我們不會詳細介紹虛擬模組和其他 TDD 技術。這個想法是為您提供 TDD 和單元測試的基本概念和原則,希望您現在已經掌握。原文 - https://simpleprogrammer.com/2017/01/30/tdd-unit-testing/
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION