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