關於單元測試體系結構的一些心得

2020-12-23 三微授漁

自動化測試是任何大型軟體項目不可或缺的一部分,可作為提高質量,生產率和靈活性的一種手段。因此,至關重要的是,系統架構的設計必須能夠促進自動化測試的開發和執行。

質量得到提高,因為自動化測試的執行可以讓我們找到,並在開發周期的早期解決問題,很多之前產品變更部署到生產和可用給最終用戶。

生產率提高是因為在開發周期中發現問題的時間越早,修復該問題的成本越低,並且不難理解為什麼。如果軟體開發人員能夠在將代碼更改集成到主存儲庫之前運行自動化測試套件,則他可以快速發現新引入的錯誤並將其修復。但是,如果沒有這樣的測試套件,則新引入的錯誤可能只會在以後由最終用戶報告的手動測試階段中出現,甚至更糟,這要求開發人員退出常規開發工作流程以進行調查和修復。

靈活性得到了改善,因為開發人員在依賴於測試覆蓋率較高的測試套件來評估其代碼更改的影響時,對重構代碼,升級程序包以及在需要時修改系統行為更有信心。

在討論自動化測試時,我也喜歡將風險管理的話題引入對話中。作為首席軟體工程師,風險管理是我工作的重要組成部分,它涉及對開發團隊進行實踐和流程指導,以減少產品技術退化的風險。從上面列出的好處中可以明顯看出,採用適當的自動化測試策略很合適,可以幫助減輕軟體項目中的風險。

展望未來,我們可以根據實現和運行自動測試的策略將自動測試分為至少三種不同類型,如以下著名的測試金字塔所示:

就使用的時間和資源而言,單元測試的開發成本低且運行成本低,並且單元測試專注於測試與外部依賴項隔離的單個系統組件(例如,業務邏輯)。

集成測試向前邁了一步,並且在不隔離外部依賴關係的情況下進行了開發和運行。在這種情況下,我們有興趣評估所有系統組件在組合在一起並面臨集成約束(例如:聯網,存儲,處理等)時是否按預期進行交互。

最後,在金字塔的頂端,圖形用戶界面測試是自動化和執行最昂貴的。他們通常依靠UI輸入/輸出腳本和回放工具來模仿最終用戶與系統圖形用戶界面的交互。

在本文中,我們將重點介紹測試金字塔的基礎,單元測試以及促進採用它們的系統體系結構注意事項。

有效單元測試的屬性

讓我們列舉一下什麼是有效的,精心設計的單元測試。以下是一個命題:

簡短,只有一個目的

簡單,清晰的設置和拆卸

快速,只需幾分之一秒即可執行

標準化,遵循嚴格的約定

理想情況下,單元測試應顯示所有這些屬性,下面我詳細說明原因。

如果單元測試不夠短,將很難閱讀並理解其目的,即確切地說是測試的內容。因此,出於這個原因,單元測試應該有一個明確的目標,並且只評估一件事,而不是嘗試同時執行多個評估。這樣,當單元測試失敗時,開發人員將更加輕鬆快捷地評估情況並進行修復。

如果單元測試需要大量的精力來設置他們的測試環境,然後將其拆除,則開發人員通常會開始質疑,花費在編寫這些測試上的時間是否值得。因此,我們需要提供一個編寫單元測試的環境,該環境要管理測試上下文的所有複雜性,例如注入依賴關係,預加載數據,清除緩存等。編寫單元測試越容易,開發人員創建它們的動力就越大!

如果執行一組單元測試要花費大量時間,則開發人員自然會減少執行頻率。這裡的危險在於擁有如此冗長的單元測試套件,以至於變得不切實際,開發人員開始跳過運行它或有選擇地運行它,從而降低了其有效性。

最後,如果測試未標準化,不久後您的測試套件將開始看起來像狂野的西部,編寫單元測試所使用的編碼風格有時會有所不同,有時會發生衝突。因此,在整個單元測試範圍內,追求系統設計的一致性對於整個系統來說同樣有效。

一旦我們對有效的單元測試的構成達成共識,就可以開始定義提升其性能的系統架構準則,如以下各節所述。

軟體複雜度

除其他因素外,軟體複雜性還源於系統中組件之間不斷增長的交互次數以及內部狀態的演變。隨著複雜度的提高,無意識地幹擾複雜的組件交互網絡的風險也隨之增加,有可能導致在更改代碼時引入缺陷。

此外,根據常識,系統的複雜性越高,維護和測試它就越困難,這導致了第一個(一般)準則:

密切關注軟體的複雜性並遵循設計實踐來控制它

在管理複雜性同時提高可測試性時,值得一提的做法是在系統設計中儘可能採用「 純函數」和「 不變性」。純函數是具有以下屬性的函數:1

對於相同的參數,其返回值是相同的(局部靜態變量,非局部變量,可變參考變量或來自I / O設備的輸入流無變化)。

它的評估沒有副作用(本地靜態變量,非本地變量,可變引用參數或I / O流不會發生突變)。

從其特性可以明顯看出,純函數非常適合於單元測試。它們的用法也消除了對許多補充性實踐的需求,這些補充性實踐將在以下各節中討論,以處理大多數有狀態的組件。

不變性同樣重要。不可變對象是創建後狀態無法更改的對象。它們更易於交互且更可預測,從而有助於降低系統複雜性,消除全局狀態。

隔離依賴

按照它們的定義,單元測試旨在隔離地測試各個系統組件,因為我們不希望組件的單元測試的結果受到其依賴項之一的影響。隔離程度會根據被測組件的具體情況以及每個開發團隊的偏好而有所不同。我個人不擔心隔離輕量級的內部業務類,因為我發現用測試目標組件替代它們並沒有增加任何價值,該組件將顯示幾乎相同的行為。儘管如此,這裡的策略可能很簡單:

在組件設計中應用依賴項反轉模式

依賴關係反轉模式(DIP)指出,高級對象和低級對象都應依賴抽象(例如接口),而不是特定的具體實現。一旦將系統組件從其依賴關係中分離出來,我們就可以在單元測試的上下文中通過簡化的,針對測試的具體實現輕鬆地替換它們。下面的類圖說明了結果結構:

在此示例中,被測組件依賴於資料庫和文件存儲抽象。當部署到生產環境中時,我們可能會為存儲庫類注入基於SQL的具體實現,並為文件存儲組件注入基於S3的實現,以便在AWS Cloud中遠程存儲文件。但是,在運行單元測試時,我們將希望注入不依賴外部服務的簡化功能實現,例如以綠色繪製的「內存中」實現。

如果您不熟悉DIP,那麼我還會發表另一篇文章,內容是有關如何在可能會有所幫助的相似上下文中使用DIP的實用概述:集成第三方模塊。

假裝與假貨辯論

請注意,我並不是將這些「內存中」實現稱為「模仿」,它們是模擬對象,它們以有限的受控方式模仿了真實對象的行為。我故意這樣做,因為我反對使用模擬對象,而建議使用完全兼容的「偽」實現,這為我們提供了編寫單元測試的更大靈活性,並且可以比設置更可靠的方式在多個單元測試類中重用模擬。

為了更詳細地說明,假設我們正在編寫一個依賴於組件的單元測試。 文件存儲抽象。在此測試中,組件將一個項目添加到文件存儲中,但實際上並不擔心操作是成功還是失敗(例如,日誌文件),因此,我們決定以「虛擬」方式模擬該操作。現在,假設稍後需求發生變化,並且組件需要確保在繼續操作之前通過從文件存儲中讀取文件來創建文件,這迫使我們更新模擬的行為以使測試通過。然後,想像需求再次發生變化,並且組件需要寫入多個文件(例如:每個日誌級別一個),而不是僅寫入一個,從而迫使我們的模擬對象行為得到另一種改善。你知道發生了什麼嗎?我們正在逐步改進我們的模擬,使其更類似於具體的實現。更糟糕的是,我們最終可能會遇到許多獨立的,半生不熟的,

為了解決這種情況,我提出以下準則:

依靠Fakes而不是Mocks來實施單元測試,將它們視為一流的公民,並將其組織為可重用的模塊

由於Fake組件實現了商業行為,因此與設置模擬相比,它們本質上是更昂貴的初始投資。但是,它們的長期回報肯定更高,並且更符合有效的單元測試的特性。

編碼風格

每個自動化測試都可以描述為三步腳本:

1、準備測試環境

2、執行按鍵操作

3、驗證結果

這是合乎邏輯的考慮,給出一個初始已知狀態,當執行一個操作,那麼就應該產生相同的預期結果,每一次。為了使結果變得不同,必須更改初始狀態,或者更改操作實現本身。

您可能對上面用黑體字標出的單詞很熟悉。如果不是這樣,它們代表了一種流行的Given-When-Then模式,以一種有利於可讀性和結構的方式編寫單元測試。這裡的想法很簡單:

為編寫單元測試定義並實施單一的標準化編碼樣式

可以使用多種方式採用「當時給定」模式。其中之一是將單元測試方法構造為三種不同的方法。例如,考慮密碼強度測試:

使用這種方法,主要的測試方法變成了對單元測試目的的三行描述,即使是非開發人員也可以通過閱讀它輕鬆地理解。在實踐中,單元測試的主要方法最終成為系統行為的低級文檔,不僅提供文本描述,還提供執行代碼,調試代碼並找出內部情況的可能性。當新開發人員加入團隊時,這對於縮短系統架構學習曲線非常有價值。

重要的是要強調,在編碼風格方面,沒有唯一正確的方法。我在上面提供的示例可能會使某些開發人員感到不滿,例如,因為冗長而令人不悅,這沒關係。真正重要的是,在您的開發團隊中就編碼慣例達成協議,以編寫對您有意義並堅持下去的單元測試。

管理測試環境

單元測試上下文管理是一個討論不夠多的主題。「測試上下文」是指成功運行單元測試所需的整個依賴項注入和初始狀態設置。

如前所述,當開發人員花費較少的時間來擔心設置測試上下文並花更多時間編寫測試用例時,單元測試會更有效。我們從以下觀察得出我們的最後一個準則,即大量測試案例可以共享一些測試上下文:

利用構建器類將測試上下文的構建與單元測試用例的實現分開

這個想法是將測試上下文的構造邏輯封裝在構建器類中,並在單元測試類中引用它們。然後,每個上下文構建器負責創建特定的測試方案,並可選地定義用於使其特定化的方法。

讓我們看一下另一個說明性的代碼示例。假設我們正在開發一個反欺詐組件,用於檢測行動應用程式用戶的可疑位置變化。測試上下文生成器可能如下所示:

由此創建的測試上下文MobileUserContextBuilder足夠通用,因此從應用程式已經註冊了移動用戶的狀態開始所需的任何測試用例都可以使用它。最重要的是,它定義了AddDevice具體化測試環境以適應我們虛擬的反欺詐組件測試需求的方法。

考慮調用此反欺詐組件GeolocationScreener,它負責檢查移動用戶的位置是否更改得太快,這表明他可能是在偽造自己的真實坐標。其單元測試之一可能如下所示:

可見,在此示例測試類中專用於設置測試上下文的代碼量很小,因為它幾乎完全包含在builder類中,從而保留了代碼的可讀性和組織性。隨著越來越多的測試用例利用可用的測試上下文構建器庫,設置測試上下文所需的攤銷時間變得非常短。

結論

在本文中,我討論了單元測試的主題,提供了五個主要指南,以應對在不斷增長的測試用例中保持有效性的挑戰。這些準則對系統體系結構有重要影響,從軟體項目開始就應該考慮單元測試要求,以促進開發人員在其中看到價值並有動力編寫單元測試的環境。

單元測試應被視為系統體系結構的組成部分,與它們所測試的組件一樣重要,而不應被視為開發團隊僅出於填寫管理報告複選框或提供指標而編寫的二等公民。

最後,如果您正在使用很少或沒有單元測試的遺留項目中,而沒有使用DIP,則由於您有意避免談論複雜的模擬框架,因此本篇文章可能沒有為您提供最佳策略。遺留項目的上下文成為將單元測試引入極度耦合的代碼的可行選擇。

相關焦點

  • 單元測試常用的方法
    單元測試的原則同樣被擴展到第四代語言(4GL)的開發中,在這裡基本單元被典型地劃分為一個菜單或顯示界面。   經常與單元測試聯繫起來的另外一些開發活動包括代碼走讀(Code review),靜態分析(Static analysis)和動態分析(Dynamic analysis)。靜態分析就是對軟體的原始碼進行研讀,查找錯誤或收集一些度量數據,並不需要對代碼進行編譯和執行。
  • Wings-面向企業級的單元測試用例自動編碼引擎
    眾所周知,軟體缺陷發現得越晚,其處理費用就越呈幾何激增,因此測試左移概念已經成為趨勢。但單元測試面臨的最大問題是:單元測試用例編寫工作量巨大,極端情況下與開發工作量比達到1:1,甚至更高,造成大量成本損耗。
  • springboot整合mybatis如何進行單元測試
    這裡主要簡單介紹了springboot整合mybatis,以及驗證是否整合成功的單元測試。使用的開發工具:Eclipse一 準備工作:1.創建一個springboot項目;其實使用eclipse創建springboot項目主要有兩種方式,一種是使用maven創建項目,只不過在pom.xml中添加一些springboot的相關依賴;另外一種是使用插件創建springboot項目。關於eclipse創建springboot項目,網上教程很多,這裡我就不細說了。
  • 系統測試:單元測試相關知識筆記
    一、單元測試概念單元測試也成為模塊測試,在模塊編寫完成且五編譯錯誤後就可以進行。單元測試側重模塊中的內部處理邏輯和數據結構。如果採用機器測試,一般用白盒測試法。二、單元測試檢查模塊特徵1、模塊接口模塊接口保證了測試模塊數據流可以正確地流入、流出。主要檢查一下要點:測試模塊輸入參數和形式參數在個數、屬性、單位是否一致。
  • 單元測試難?來試試這些套路
    在經過一些項目的實踐後,也是有了一些自己的理解和實踐,希望和大家分享一下,和大家探討下如何克服「單元測試」的心魔。文末福利:開發者成長計劃,最強助力!內功前人們在單元測試方面的研究很多,有很多的方法論,我們可以拿來即用。我簡單介紹兩個方法論,一個概念。
  • 單元測試 vs 集成測試,你該怎麼選?
    在我看來,集成測試和單元測試是健壯軟體的基石。因此,今天讓我們看看單元測試與集成測試之間的區別,以及你什麼時候該選擇哪種測試。什麼是一個單元?一個單元是邏輯上分離的最小代碼塊單元測試是一種孤立地測試儘可能小的代碼片段的測試。那麼,什麼是一個單元?術語「單元」來自數學。數字 1 被認為是單元,因為它是最小的自然數。它是最小的正整數。
  • 三年級上冊語文第一單元專題訓練:附每課課後練習+單元測試卷!
    三年級上冊語文第一單元專題訓練:附每課課後練習+單元測試卷部編版三年級上冊語文第一單元以「學校生活」為主題,包含三篇課文,其中兩篇精讀課文《大青樹下的小學》、《花的學校》和一篇略讀課文《不懂就要問》。關於這三篇課文的詳細知識總結,我們之前就為同學們做了分享,同學們可以去翻一翻之前的文章,把每課的知識都掌握好。
  • Java 單元測試
    單元測試的框架:Junitjava 語言編寫第三方單元測試框架單元:java中一個類就是一個單元單元測試步驟書寫業務類書寫測試類,一般命名規則為Test + 業務類名,測試方法,一般命名規則為test + 方法名, 內部可以使用Assert斷言測試方法要求:選中方法名,運行測試方法,選中類名,執行測試類中所有的方法選中模塊名,運行模塊中所有測試類的所有測試方法
  • 未名企鵝極客|軟體單元測試的基本原則
    本期未名企鵝極客欄目,研發工程師給大家分享的是一些單元測試的基本原則。 4、測試顆粒度要足夠小 編寫單元測試時要保證測試粒度足夠小,這樣有助於精確定位問題,單元測試用例默認是方法級別的。單元測試不負責檢查跨類或者跨系統的交互邏輯,那是集成測試需要覆蓋的範圍。
  • 經驗教程 I iOS單元測試
    而iOS非常幸運,蘋果開發工具Xcode在創建項目是就能夠自帶XCTest,包含單元測試和UI測試,這次我們從兩個方面講一下單元測試。對文件夾右鍵點擊NewFile,選擇Unit Test Case Class即可創建一個單元測試文件創建過程就到這裡了。接下來針對單元測試一些使用進行簡單描述。
  • MyEclipse中Java單元測試的使用
    在使用MyEclipse進行Java開發中,做單元測試是很有必要的,接下來簡單介紹下其是如何使用的。,然後勾選setUpBeforeClass和tearDownAfterClass,然後點擊Finish,如下圖所示:7、此時提示添加JUnit4庫到項目中,點擊OK,如下圖所示:8、我們在setUpBeforeClass和tearDownAfterClass方法中增加一個輸出內容,然後添加兩個簡單的測試方法並輸出一些內容
  • 基於django的單元測試
    【知道】認識單元測試單元測試:測類、方法、函數,測試最小單位由於django的特殊性,通過接口測單元,代碼邏輯都放在類視圖中單元測試好處消滅低級錯誤快速定位bug(有些分支走不到,通過單元測試提前測出問題)提高代碼質量(測試後順便優化代碼)2.
  • 用Jasmine和Blanket編寫基於Web的客戶端單元測試
    在本文中,我們將介紹兩種基於JavaScript的編寫單元測試的軟體包,它們背後的代碼以及最佳使用方式。前言在編寫一個網站時,或者更經常地 - 一個單頁的應用程式 - 需要測試它,就像其他任何代碼一樣。當然有幾種類型的測試,包括單元測試和集成測試。
  • JUnit 5.6版本帶來了Java單元測試的新功能
    Java單元測試框架在v5.6中具有性能改進,新功能和較小的錯誤修復,但是還添加了重大更改。讓我們仔細看看,看看JUnit的三個模塊發生了什麼變化。用於在JVM上進行開發人員端測試的Java單元測試框架由三個模塊 JUnit Platform,JUnit Jupiter和JUnit Vintage組成。在v5.6中,所有模塊均已升級。JUnit平臺JUnit平臺具有新功能和重大更改。
  • 3個C 單元測試工具,到底誰才是王者?
    MsTest V2主要是為了.net core準備的,也可以在.net framework上運行,並且較V1版本新加入了一些擴展。下圖是VsTest的總體架構圖,從圖中可以看到,整個VsTest體系架構主要有四個組件:測試運行器、測試執行器、數據收集器和IDE。
  • 「融雲分析」關於測試驅動開發的一些感悟
    再紛繁複雜的業務邏輯也能按功能、層級分為若干業務模塊,模塊與模塊之間通過接口(API)通信,做好這兩點的設計無形中也就降低了業務邏輯的耦合性,低耦合又是單元測試的前提條件。這樣操作等同於迫使開發者將接口的設計與低耦合性放在第一位去考慮。工作中在接到項目、明確需求後,如何分配任務往往非常考驗團隊人員的綜合能力,拆解顆粒度太粗容易造成邊界不明確;太細又牽扯過多精力,不易實施。
  • 軟體測試學習教程:單元測試之UnitTest測試框架
    單元測試的概念單元測試(unit testing),是指對軟體中的最小可測試單元進行檢查和驗證。對於單元測試中單元的含義,要根據實際情況去判定其具體含義。一個單元可能是功能模塊、類、方法(函數)等。單元測試工具不同的程式語言都有比較成熟的單元測試框架,語法規則有些差別,其核心思想都是相通的。
  • 阿里新型單元測試 Mock 工具開源
    基於動態字節碼修改實現的JMockit要技高一籌,它在不影響測試覆蓋率的情況下,僅通過「局部手術」就能讓被測方法裡的Mock目標「狸貓換太子」。不過,JMockit不僅要求每個用例的開頭和結尾採用固定結構,而且發明了一種並不太符合Java習慣的Mock定義語法,妥妥的將自己做成了一款「測試框架」。 同樣看個例子。
  • 軟體測試中黑盒測試和白盒測試的詳細講解
    本篇將介紹關於軟體測試的黑盒測試和白盒測試知識的分享,有興趣的朋友可以了解一下!眾所周知,軟體測試是為了便於程式設計師對必要的有要求的軟體進行相關的運行和闡明,判辯設計的規範以及最後運行成功後的審查編碼的合格性。這一步的成功也就是設計的成功的第一步。我們將軟體的測試分為兩個模塊來完成,一方面是程序的編碼部分設計與軟體單元測試的部分,另一個方面就是對整個設計的全面檢測了。
  • 二年級語文上冊:第七單元測試卷,含答案,收藏練習,鞏固基礎
    這個學期已過大半,孩子已經開始上第七單元了,為了幫助孩子鞏固基礎知識,今天給大家分享人教版二年級上冊語文第七單元測試卷。這套試卷集合了本單元的重點難點知識,覆蓋的知識面廣,很適合二年級的學生練習。感興趣的家長可以提前收藏起來,讓孩子做測試,鞏固基礎知識。在文末附有答案!第一大題考查學生拼音和生字詞方面的知識。這道題答案分別是:田野、蒼茫、散步、水汽、長久、旁邊、唱歌、渾身。第二大題考查學生查字典,生字部首、筆畫、結構、讀音、組詞等。