體面編碼之異常日誌和測試處理

banq發表於2018-12-31

在方法介面的域中丟擲異常。這可以避免破壞抽象或應用程式層。例如,DAO不應丟擲HTTP異常,也不應傳播JDBC異常。可以捕獲異常並將其包裝在更合適的位置以便於實現此目的。

要麼用日誌記錄異常,要麼丟擲它們 - 通常不是兩者。丟擲的異常將在某種程度上被捕獲; 如果較低階別的投擲者無法處理異常,則可能不會最好決定是否應該記錄該異常,並且在上下文中提供資訊。廣泛的log-and-throw也會導致重複日誌記錄,因為異常會使呼叫層次結構冒泡。規則的一個例外是當更高階別超出我們的控制範圍(例如框架)時 - 並且不記錄,記錄在不合需要的級別,或者不包括足夠的細節。

從一個無法完成其名稱承諾的方法中丟擲異常。該名稱表達了期望,如果無法滿足,則需要通知呼叫者。

避免日誌記錄錯誤並繼續執行。這種做法不是有意義的錯誤處理,並且可能導致後續錯誤和損壞。當前上下文中的執行應該停止,或採取一些備用恢復路徑。

在異常/日誌訊息中包含相關和上下文資訊。這些資訊有助於診斷問題。示例包括有問題的值,狀態和識別符號。對於自定義異常,可以在建構函式中強制使用某些資訊,而不是接受單個字串訊息的常見做法。

考慮catch-wrap-rethrow新增更有用的訊息和/或上下文資訊。原始丟擲者可能沒有太多的背景或資料來構建特別有用的訊息。此類丟擲者的呼叫者可以捕獲此類異常並將其包含在更具資訊性的異常中,然後再將其拋棄。

通常,日誌記錄完全記錄異常。僅記錄通用訊息或僅記錄捕獲的異常訊息,會丟棄可能有用的資訊 - 包裝的cause-exceptions和堆疊跟蹤的訊息。在系統邊界處(例如,當暴露API時),通常希望省略(或在較低階別登入)客戶端錯誤的細節(例如,請求驗證)以避免過多的對數噪聲。

Web API
以適當的錯誤響應程式碼,以及包含更多詳細資訊的資訊機構。這使客戶能夠快速識別問題的性質,而無需查閱服務日誌。例如,響應可能表示錯誤的請求以及對錯誤的解釋。

避免在錯誤響應中洩露(或製作可感知的)敏感資訊或實現細節。敏感資訊是客戶不應該知道的任何資訊(即使它沒有顯示在他們正在使用的UI中),例如他們無法訪問的資料的存在,或者現有的限制/限制在他們的帳戶上。顯示實施細節可以幫助攻擊者,無論是應用程式還是組織中的其他人。許多Web框架具有用於集中且一致地處理該問題的特徵和/或鼓勵模式。

使用者介面
向使用者顯示適當且有用的資訊。主體應該以非技術術語來表達問題所在,使用者正在做什麼的當前狀態以及糾正問題的途徑。技術細節可能包含在可揭示的區域中,以包含在錯誤報告中。

如果問題是暫時的,請僅讓使用者再試一次。在連線問題可能正常後再次嘗試。再次嘗試使用相同的無效表單輸入肯定不會。

日誌記錄
請記住日誌記錄的目的。主要是:確定應用程式是否執行順暢,並診斷問題是否執行不順暢。記住這些有助於確定我們是否應該記錄,如果是,應該包括哪些資訊。INFO應用程式正在使用時應該有一些“tickover” 日誌記錄,表明一切都很好。當事情不好的時候,應該注意WARN並ERROR記錄注意力,並詳細說明問題所在。

遵循應用程式約定的時間/內容/方式。這可確保應用程式的所有區域始終如一。

避免瑣碎,不相關或重複的日誌記錄。這種記錄只是噪聲,有損於實際重要記錄的“訊號”。

讀取日誌記錄輸出以確保“流量”。它應該連貫地閱讀,作為正在發生的事情的故事。在執行特定的應用程式任務時嘗試閱讀它,並且正在進行負載測試。

避免緊密耦合的日誌記錄協作和依賴項。日誌訊息應該最多能夠獨立存在。如果修改或刪除其他遠端日誌呼叫,它們應保持有意義; 也就是說,完整的日誌輸出不應該是脆弱的。避擴音及,設定期望,與遠端日誌記錄合作構建多訊息“句子”或“關閉您開始的業務”。

日誌內容是寫清楚,簡潔,明確的訊息。遵循這些原則的訊息更快更容易理解,並有助於避免誤解或混淆。

包括相關和上下文資訊。這些資訊有助於診斷問題。示例包括鍵值,狀態和識別符號。為了便於搜尋並允許透過日誌檢視工具進行解析,請考慮使用諸如的模式key=value | other=value。

使用分隔符區分訊息文字中的值。當它們嵌入在訊息中時,某些型別的資料很難與日誌訊息模板本身區分開來。使用分隔符(如引號,大括號或尖括號)可在必要時闡明邊界。

使用對映的診斷上下文(MDC)來區分多個執行緒的日誌記錄。當多個併發執行緒的記錄輸出交錯時,幾乎不可能理解正在發生的事情。在各個日誌語句中包括諸如使用者/請求ID之類的上下文資訊是繁瑣且重複的。相反,配置記錄器的模式以在每條線上包含此類資訊,就像在時間戳中一樣。

日誌應該設定適當和一致地使用水平。建立(或獲得)指南,並遵循它們。例如,當應用程式平穩執行時,它不應該噴出大量的錯誤和警告。

客戶端錯誤不是應用程式錯誤。如果客戶端傳送無效請求或以其他方式執行非法/錯誤的操作,那就是錯誤的客戶端 - 而不是處理請求的應用程式。記錄應用程式錯誤等錯誤會在行為不當或執行不當的客戶端呼叫我們的應用程式時產生噪音。

測試
測試也是程式碼。本指南中其他主題的專案適用。它們需要進行程式碼審查,並且必須遵循與主程式碼相同的自動和手動質量檢查和規則。糟糕的測試程式碼不太可靠,並且可能難以更改或重構主程式碼。

保持測試程式碼接近他們正在測試的程式碼。閱讀:檔案

每次測試的單一焦點和目的。範圍廣泛的測試不太清楚,並且更難以確定故障原因。針對僅與測試名稱中描述的情況相關的故障,並根據需要進行斷言。

平衡價值與成本。測試需要努力編寫和維護。考慮一段給定的程式碼(無論多小),如果你在正確性方面獲得的信心以及避免未來的破壞,那麼它與測試的耗時和難度相比是值得的。這個專案不是使程式碼難以測試的反模式的藉口。

覆蓋範圍是一個指南; 它並不意味著充分性和不充分性。覆蓋率表示在執行測試時透過程式碼的路徑。它沒有說明好的測試用例選擇或做出適當的斷言。對提供高覆蓋率的程式碼的測試可能需要改進,並且對提供較低覆蓋率的程式碼的測試可能是完全足夠的。避免編寫低價值測試以提高覆蓋率指標。

避免測試程式碼“它實現什麼功能就測試什麼功能”的測試。這樣的測試通常會檢查某些模擬被呼叫,並且不測試被測單元的任何邏輯(如果單元甚至有任何邏輯)。它們很脆弱,是重構,噪音和低價值的開銷。

避免測試模擬庫的測試。通常是偶然的,這樣的測試不會斷言我們的程式碼,而是例如檢查方法存根功能是否有效。

包括邊緣情況和不愉快路徑的測試。我們需要確信我們的程式碼在所有情況下都能正常工作,而不僅僅是絕大多數情況下可能出現的“正常”程式碼。示例包括錯誤,超時,無效資料,無資料和邊界值。

避免測試之間的共享狀態。共享狀態會破壞測試隔離,通常會導致涉及錯誤傳遞或錯誤失敗的混亂,具體取決於哪些測試一起執行以及以何種順序執行。示例包括共享變數(通常是為了避免重新宣告),重用測試例項和重用的模擬。始終重新開始,或在模擬的情況下,重置它們。

避免鉤子中的共享設定邏輯。這意味著測試框架在單個測試集合之前執行的程式碼塊。考慮到極端,它涉及巢狀的測試組,每個級別都有一個共享的設定塊。它鼓勵使用共享狀態,允許意外使用共享狀態,使設定難以遵循,使得單個測試的定製設定變得困難,並且使得難以修改測試並新增新測試。

實用函式中的抽象通用設定邏輯。這避免了使用鉤子引起的所有問題。將共享設定邏輯提取到實用程式功能中,並根據需要直接使用它們。這些函式返回準備在測試中使用的物件,並且通常接受引數(資料或選項)以允許為各個測試定製設定。後者可以很容易地看到每個測試之間的設定差異。

避免依賴當前系統時間和時區。此類測試不會在每次執行時測試相同的內容,並且可能在將來或由不同位置的人員或CI伺服器執行時導致問題。將當前時間注入程式碼中,允許在測試中注入特定的固定時間。

斷言
使用嚴格的斷言。這些加強了測試,使其更有可能發現未來的迴歸。示例包括嚴格相等,以及具有特定訊息的特定型別的錯誤/異常。

使用最合適的斷言。測試框架包括除了相等之外的斷言。他們澄清測試程式碼,併產生更好的失敗訊息。
確保實際執行非同步程式碼中的斷言。使用特定於測試框架的技術來確保它們執行。一些框架提供了同步斷言,斷言後來的斷言實際上已經完成。

其他測試
模擬直接依賴而不是傳遞依賴。這使測試保持獨立並專注於單個單元。

避免將模擬執行時資料重用為模擬測試資料。這些資料是為不同目的而設計的(通常是演示),並且應該保持可修改而不影響測試。

避免等待經過的時鐘時間。對於非同步程式碼和計時器等待這種方式的測試執行緩慢; 當你有數百個時,這就成了一個大問題。相反,使用您的測試框架的時間模擬工具,可以立即提前。

相關文章