2015-06-03 77 views
1

這裏是我的高層次,一般的問題:Java的斷言,在比較/對比單元測試和異常

一個爲什麼要使用斷言,而不是單元測試和異常處理?

爲了澄清我的疑惑並限定我的顧慮,我會談一談我對Java斷言和我的背景的瞭解,因爲它與這一問題相關。所以,要開始,在本週之前,我真的不知道Java框架的斷言,也不知道它們的功能,所以我絕對承認我很可能沒有得到全部圖像。話雖如此,我近十年來一直是Web開發,數據和API開發方面的專家,所以我並不完全天真。

從我讀過的和討論過的內容來看,斷言通常在開發過程中使用,(幾乎)在生產中總是關閉,並檢查後期條件和類不變值;他們可以被用於其他事情,但這兩件事似乎是他們當前的主要用法。它們構建在Java異常之上,是Error的子類型,並在違反時引發。關閉時,它們不會增加運行時間開銷。但是,對我而言,它們看起來是多餘的,特別是在考慮良好的異常處理技術和良好的單元測試實踐時。我沒有看到任何情況下,斷言會提醒程序員注意異常處理或單元測試無法解決的錯誤或問題。

遵循這一思路,他們似乎在創造代碼混亂。如果他們不打算在生產環境中運行並充當開發援助,那麼他們在生產代碼中的存在就有點反模式:你有單獨的源文件夾用於測試和測試資源,而你編寫可測試的代碼,而是而不是代碼嚴格的測試。每當我發現自己編寫的代碼只是爲了測試,就會提醒我設計不佳,然後我退後一步,重新設計我正在做的事情,以便我的代碼可以測試。在我看來,這是這種範例被侵犯的一個例子。無論存在什麼斷言,它都可以被try/catch塊取代,如果/ then/else拋出了適當的異常,或者通過一組單元測試 - 至少,這就是我認爲的方式。在生產代碼中聲明不會在生產過程中運行,這是令人困惑的。它也會干擾代碼的目的和代碼的功能。我發現一個測試可以評估相同的條件,斷言會更加可讀,因爲它會被包含在一個測試中,該測試可以爲該確切場景命名和記錄。

其他人可以增加洞察這一思路?也許你同意 - 添加例子爲什麼或闡述我的推理。再說一遍,也許你認爲我錯了 - 陳述原因並提供例子來支持你的推理,或者提供反面的觀點來看待我的觀點。

謝謝大家!


資源我請教,供大家參考:

+0

一般來說,你可能是對的。你可以爲每個「testing」assert編寫一個JUnit測試,並且你可以用try-catch或者throw來代替每個「functional」assert。但是對於快速代碼抽樣,編寫'assert'比編寫完整的測試用例要容易。也許這就是Java'assert'關鍵字的利基...... – Turing85

+0

「用於快速代碼抽樣,比編寫完整的測試用例更容易編寫斷言」 - 比另一個更爲正確,或者它們是否有不同/補充用例?在我看來,從測試和生產代碼明顯分離的角度來看,單元測試會更加正確,並且明確地拋出一個詳細且恰當命名的異常會更清晰和更具可讀性。在這一點上我只是在qu?? – liltitus27

+0

這正是我所說的*快速代碼抽樣*。單元測試更精細,代碼更昂貴,以及'try-catch'塊和propper異常處理。 – Turing85

回答

2

使用斷言應該涵蓋發生的不可能的不可能的條件。當不可能發生持續的時間時,最好大聲死亡,並在後一點提供不正確的結果或不相關的例外情況。單元測試不是真正的替代品,因爲你不能用它們來證明一些條件可以發生而不是

其中一個典型用途是switch聲明。如果您確信case條款涵蓋了所有可能的狀態,則可以將default塊保留爲空,並且希望您的產品正確(並且在周圍代碼更改時您仍然是正確的),或者在其中放置斷言,以便通知您如果你錯了。

無論您是否決定使用Java的assert語句(可以按照您的說法關閉)或直接拋出AssertionError(這是無法關閉的東西)完全取決於您。

+0

「單元測試不是真正的替代方法,因爲你不能用它們來證明某些條件不可能發生」 - 我認爲這是我沒有把握的一個概念。一個斷言如何證明一個單元測試不能發生某種情況?關於switch語句,爲什麼我不想明確地拋出InvalidArgumentException等,而不是assert,這可能會在生產中被關閉? – liltitus27

+0

我不是說你可以用斷言來證明永遠不會發生的事情(或者總是會發生的事情)。我建議使用斷言表達這樣的假設,以便以可見的方式對其進行反駁(與日誌消息或評論相比)。 –

+0

Java的'InvalidArgumentException'表示客戶端將無效值傳遞給您的代碼。儘管由於編程錯誤,這些值來自您自己的代碼,但這沒有意義。 –

2

本主題是關於意見,所以我只是添加了一些關於該主題的想法。這些不包括所有方面,但可以補充他人的意見。

有時我使用斷言來表達我在編寫代碼時做出的一些假設。簡單的例子:我打電話給方法來獲得一般可以是null例如通過名稱找到用戶,但在這種特殊情況下,這不會發生,例如,我正在註冊。因此,我跳過代碼「空檢查」,但我要明確誰是永遠之後(也許我在一個月:)),我認爲這個選項

User user = findUser(userId); 
assert user != null : "we are in the middle of registration" 
user.confirmEmail(); 

很顯然,我可以讀我的代碼使用簡單的評論來做到這一點,但在代碼混亂的時候並沒有太大的壞處。

正如您已經提到的,第二種用例是檢查不變量。 「學校示例」就是您正在實施合併排序的情況,並且在合併階段期間,算法期望您要合併的兩個子數組已經排序(按照前面的步驟)。

很明顯,你會實現單元測試來檢查排序是否正常工作,但這隻能驗證「端到端」的正確性。對於大多數情況來說這很好,但是當你正在實施它時,你會發現你的一個測試失敗了。不幸的是,這個測試使用「大量輸入」,並且所有「簡單」測試都通過了,所以調試問題是不切實際的。由於合併類沒有狀態,只能用於遞歸和局部變量,所以不能從外部單元測試中反省狀態。這是您可以使用斷言來檢查內部不變量的地方。一旦不變失敗,你將得到堆棧跟蹤(因此你發現在遞歸的第四級有一個問題),你可以將重要本地變量的實際狀態添加到消息中,這樣可以幫助你識別錯誤。這裏斷言是很好的,因爲如果在生產期間對子陣列進行排序,您不想「重新檢查」,因爲這樣的檢查可能非常昂貴。

修復代碼後,您可能會試圖刪除此類代碼,但這可能取決於具體情況。我們爲什麼首先編寫單元測試?因爲如果我們重構,我們想確定我們是否沒有破壞任何東西。如果你期望你可能想要重構代碼,並且你所做的不變式可以保留下來,你可能希望保留它們以備將來使用。否則,您可以簡單地刪除它們或根本不使用斷言,只需使用一些臨時代碼來查找問題。

+0

「很明顯,我可以使用簡單的評論來做到這一點,但在代碼混亂的時候並沒有太大的壞處。」我可以看到這一點。我最初的傾向是不同意,但退後一步,我可以看到,作爲文檔的功能,你是對的。我認爲就評論而言,至少在某些情況下,這只是習慣將斷言視爲(類型)評論的問題。 – liltitus27