只能在测试中注射吗?
本文是關于測試設計和可測試性的一些想法。 我們與我的兒子討論了一些問題,他的兒子是Java的初級開發人員,目前在匈牙利的EPAM(我工作的同一家公司,但在另一家子公司)工作和學習。 本文中的所有內容都是不錯的舊知識,但是,您仍然可以在其中找到一些有趣的東西。 如果您是初中生,那么就是這個原因。 如果您是大四學生,那么您將獲得一些有關如何解釋這些事情的想法。 如果都不是:對不起。
問題簡介
他們要做的任務是一些輪盤賭程序或其他游戲模擬代碼,他們必須編寫。 代碼的輸出是損失或贏得的模擬錢數。 該模擬使用隨機數生成器。 在進行測試時,該生成器引起了頭痛。 (是的,您是對的:問題的根本原因是缺乏TDD。)代碼的行為是隨機的。 有時,模擬玩家贏得了比賽,而其他時候卻輸了。
使它可測試:注入模擬
如何使此代碼可測試?
答案應該很明顯:模擬隨機數生成器。 利用注入的隨機源,并在測試過程中注入不同的非隨機源。 在測試期間,隨機性并不重要,因此無需測試隨機性。 我們必須相信隨機數生成器是好的(不是,它永遠不會好,也許足夠好,但這是完全不同的故事),并且已經由其自己的開發人員進行了測試。
學習#1:不要測試依賴項的功能。
我們可以將Supplier類型的字段初始化為() -> rnd() lambda之類的字段,如果進行測試,則使用setter覆蓋它。
可測試的好嗎?
現在,我們更改了類的結構。 我們打開了一個新條目,以注入一個隨機數生成器。 這個可以嗎?
沒有普遍的是或否的答案。 這取決于要求。 程序員喜歡使其代碼可配置,并且比當前要求所絕對需要的代碼更具通用性。 原因……好吧……我想,這是因為過去,程序員多次經歷了需求的變化(開玩笑!),并且如果為變化做好了準備的代碼,那么編碼工作就變得容易了。 這是足夠合理的推理,但其中存在一些基本缺陷。 程序員不知道將來會出現什么樣的需求。 通常,沒有人真正知道,每個人對此都有一些想法。
程序員通常知識最少。 他們怎么知道未來? 業務分析師了解得更好一些,并且在鏈的末端,用戶和客戶最了解它。 但是,即使他們也不知道自己無法控制的業務環境也可能需要程序的新功能。
另一個缺陷是,開發未來需求現在會產生很多開發人員無法理解的額外成本。
實踐表明,這種“提前”思考的結果通常是幾乎不需要的復雜代碼和靈活性。 甚至有一個縮寫詞: YAGNI ,“您將不需要它”。
那么,實現該可注射性功能是否為YAGNI? 一點也不。
首先:代碼有許多不同的用途。 執行只是一個。 同樣重要的是代碼的維護。 如果無法測試該代碼,則無法可靠地使用它。 如果無法測試代碼,則無法對其進行可靠的重構,擴展:維護。
僅用于測試的功能就像房子的屋頂橋。 您在房屋中時不會自己使用它,但是如果沒有它們,檢查煙囪將非常困難且昂貴。 沒人質疑這些屋頂橋的必要性。 它們是必需的,它們是丑陋的,而且仍然存在。 沒有他們,房子就無法測試。
學習#2:可測試的代碼通常具有更好的結構。
但這不是唯一的原因。 通常,當您創建可測試的代碼時,最終結構通常也將更有用。 也就是說,可能是因為測試模仿了代碼的使用,而設計可測試的代碼將促使您將可用性放在第一位,將實現放在第二位。 而且,說實話:沒有人真正在乎實施。 可用性是目標,實現只是實現目標的工具。
責任
好的,我們做到了:可測試性很好。 但是,還有一個關于責任的問題。
隨機性的來源應該硬連接到代碼中。 代碼和代碼的開發者負責隨機性。 不是因為這個開發者實現了它,而是因為這個開發者選擇了隨機數生成器庫。 選擇基礎庫是一項重要的任務,必須負責任地完成。 如果我們打開一扇門改變隨機性的實現選擇,那么我們將失去對我們責任的控制。 還是不是?
是的,沒有。 如果您打開API并提供了注入依賴項的可能性,那么您就不必對注入的功能的運行負責。 盡管如此,用戶(您的客戶)仍會來找您尋求幫助和支持。
“有一個bug!” 他們抱怨。 是因為您的代碼還是用戶選擇的特殊注入實現中的某些內容?
您基本上有三個選擇:
第一種方法需要良好的銷售支持,否則您最終將花費個人時間解決客戶問題,而不是花費您的付費客戶時間。 不專業。
第二種方法是專業的,但客戶不喜歡它。
第三是將用戶從#1吸引到#2的技術解決方案。
學習#3:提前考慮用戶的期望。
無論選擇哪種解決方案,重要的事情都是有意識地做到,而不僅僅是偶然。 了解您的用戶/客戶可能會想到什么并做好準備。
防止生產注入
當您打開將隨機性生成器注入代碼的可能性時,如果確實需要,如何為生產環境關閉那扇門?
我首選的第一個解決方案是,首先不要將其打開。 通過具有lambda表達式(或其他方式)的初始化字段使用該表達式,使其可以注入,但不實現注入支持。 讓該字段為私有字段(但不是最終字段,因為在這種情況下可能會導致其他問題),并在測試中進行一些反思以更改私有字段的內容。
另一個解決方案是提供一個包私有的setter,或者更好的方法是提供一個額外的構造函數來更改/初始化字段的值,并在生產環境中使用它時引發異常。 您可以檢查很多不同的方式:
- 為生產環境中不在類路徑上的測試類調用`Class.forName()`。
- 使用`StackWalker`并檢查調用者是否為測試代碼。
為什么我更喜歡第一個解決方案?
學習#4:不要僅僅因為可以就使用花哨的技術解決方案。 無聊通常會更好。
首先,因為這是最簡單的方法,所以會將所有測試代碼放入測試中。 應用程序代碼中的設置程序或特殊構造函數本質上是測試代碼,而生產代碼中則包含它們的字節代碼。 測試代碼應在測試類中,生產代碼應在生產類中。
第二個原因是設計功能在生產環境和測試環境中故意有所不同,這恰恰違背了測試的基本原理。 測試應在經濟上盡可能模擬生產環境。 當測試環境不同時,您如何知道代碼將在生產環境中正常工作? 你希望。 已經有許多環境因素可能會改變生產環境中的行為,并讓bug僅在測試環境中表現出來而無聲地保持休眠狀態。 我們不需要額外的這類東西來使我們的測試更具風險。
摘要
編程和測試還有更多方面。 本文僅討論討論中出現的一小部分特定問題。 文章中還列出了一些重要的經驗教訓:
- 測試被測系統(SUT),而不是依賴項。 注意,實際上在測試某些依賴項的功能時,您可能會認為您正在測試SUT。 使用愚蠢而簡單的模擬。
- 遵循TDD。 編寫測試之前并與功能開發混在一起。 如果不只是因為您不這樣做而已,那么至少在編寫代碼之前和同時考慮一下測試。 可測試的代碼通常更好(不僅僅是測試)。
- 考慮一下其他程序員將如何使用您的代碼。 想象一下,一個普通的程序員如何使用您的API并不僅為像您這樣的天才產生代碼的接口,他們比您更了解您的意圖。
- 大三的時候,不要僅僅因為可以就去尋求理想的解決方案。 使用無聊且簡單的解決方案。 您將知道您何時是大四學生:什么時候不再想用無聊的解決方案了。
翻譯自: https://www.javacodegeeks.com/2019/07/inject-able-only-test.html
總結
以上是生活随笔為你收集整理的只能在测试中注射吗?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 计算机图形学论文_论图计算
- 下一篇: 华为手机辅助小球在哪