為什么要編寫單元測試–測試技巧8

我對最近在“您應該測試什么”上的博客有很多反應,有些人出于各種原因同意我的想法,另一些人則認為建議某些類可能不需要單元測試是非常危險的。 已經處理了什么測試,今天的博客涉及為什么要編寫單元測試,而今天的示例代碼是基于一個真實的故事:只有姓名,日期和事實已經改變。

一位客戶最近請求緊急釋放一些代碼,以出于法律原因在其網站的相應頁面上在屏幕上顯示一條消息。

方案是,如果一條信息存在于數據庫中,則應該在屏幕上顯示一條信息–這是一個非常簡單的方案,可以用幾行簡單的代碼行覆蓋。 但是,在急于編寫代碼的過程中,開發人員沒有編寫任何單元測試,并且代碼中包含一個直到補丁到達UAT才發現的錯誤。 您可能會問到錯誤是什么,這是我們在職業生涯中某個時候都已經完成的事情:添加不需要的分號';'。 到一行的結尾。

我將使用我以前的“測試技術”博客中使用的AddressService場景來演示代碼的重寫特技雙重版本,如下UML圖所示:

在此演示中,功能已更改,但是邏輯和示例代碼結構基本上保持不變。 在AddressService世界中,邏輯運行如下:

  1. 從數據庫獲取地址。
  2. 如果地址存在,則將其格式化并返回結果字符串。
  3. 如果地址不存在,則返回null。
  4. 如果格式化失敗,請不要擔心,并返回null。

重新編寫的AddressService.findAddress(…)看起來像這樣:

@Component
public class AddressService {private static final Logger logger = LoggerFactory.getLogger(AddressService.class);private AddressDao addressDao;public String findAddressText(int id) {logger.info("In Address Service with id: " + id);Address address = addressDao.findAddress(id);String formattedAddress = null;if (address != null);try {formattedAddress = address.format();} catch (AddressFormatException e) {// That's okay in this business case so ignore it}logger.info("Leaving Address Service with id: " + id);return formattedAddress;}@Autowired@Qualifier("addressDao")void setAddressDao(AddressDao addressDao) {this.addressDao = addressDao;}
}

您發現錯誤了嗎? 當我查看代碼時,我沒有……為了以防萬一,我在下面注釋了一個屏幕截圖:

演示一個瑣碎的錯誤(任何人都可以犯的一個簡單錯誤)的目的是強調編寫一些單元測試的重要性,因為單元測試可以發現問題并節省大量的時間和費用。 當然,每個組織都不同,但是發布上面的代碼會導致以下事件序列:

  • 該應用程序已部署到開發,測試和UAT。
  • 測試團隊檢查了修改后的屏幕是否可以正常工作并通過了更改。
  • 其他屏幕經過回歸測試,發現不正確。 記錄所有失敗的屏幕。
  • 提出了緊急的錯誤報告。
  • 該報告通過各個管理級別。
  • 該報告已傳遞給我(我想念午餐),以調查可能的問題。
  • 該報告將發送給團隊的其他三名成員進行調查(四雙眼睛比一只更好)
  • 找到并修復了有問題的分號。
  • 該代碼在dev中進行了重新測試,并簽入到源代碼管理中。
  • 該應用程序已構建并部署到開發,測試和UAT。
  • 測試團隊檢查修改后的屏幕是否可以正常工作并通過更改。
  • 其他屏幕經過回歸測試并通過。
  • 緊急修復程序已通過。

上述一系列事件顯然浪費了大量的工時,浪費了大量現金,不必要地提高了人們的壓力水平,并破壞了我們在客戶中的聲譽: 所有這些都是編寫單元測試的很好理由。

為了證明這一點,我編寫了三個缺失的單元測試,只花了我額外的15分鐘開發時間,與浪費的工時相比,這似乎是開發人員時間的一種很好的利用。

@RunWith(UnitilsJUnit4TestClassRunner.class)
public class WhyToTestAddressServiceTest {private AddressService instance;@Mockprivate AddressDao mockDao;@Mockprivate Address mockAddress;/*** @throws java.lang.Exception*/@Beforepublic void setUp() throws Exception {instance = new AddressService();instance.setAddressDao(mockDao);}/*** This test passes with the bug in the code* * Scenario: The Address object is found in the database and can return a* formatted address*/@Testpublic void testFindAddressText_Address_Found() throws AddressFormatException {final int id = 1;expect(mockDao.findAddress(id)).andReturn(mockAddress);expect(mockAddress.format()).andReturn("This is an address");replay();instance.findAddressText(id);verify();}/*** This test fails with the bug in the code* * Scenario: The Address Object is not found and the method returns null*/@Testpublic void testFindAddressText_Address_Not_Found() throws AddressFormatException {final int id = 1;expect(mockDao.findAddress(id)).andReturn(null);replay();instance.findAddressText(id);verify();}/*** This test passes with the bug in the code* * Scenario: The Address Object is found but the data is incomplete and so a* null is returned.*/@Testpublic void testFindAddressText_Address_Found_But_Cant_Format() throws AddressFormatException {final int id = 1;expect(mockDao.findAddress(id)).andReturn(mockAddress);expect(mockAddress.format()).andThrow(new AddressFormatException());replay();instance.findAddressText(id);verify();}
}

最后,冒著自鳴得意的風險,我不得不承認,盡管在這種情況下該錯誤不是我的,但在我學會編寫單元測試之前,我過去曾大范圍地發布了類似的錯誤……
可從GitHub上獲得源代碼:

git://github.com/roghughe/captaindebug.git

參考: 為什么要編寫單元測試-來自JCG合作伙伴 Roger Hughes的測試技巧8 ,位于Captain Debug's Blog中 。

相關文章 :

  • 測試技巧–不編寫測試
  • 端到端測試的濫用–測試技術2
  • 您應該對什么進行單元測試? –測試技術3
  • 常規單元測試和存根–測??試技術4
  • 使用模擬的單元測試–測試技術5
  • 為舊版代碼創建存根–測試技術6
  • 有關為舊版代碼創建存根的更多信息–測試技術7
  • 一些定義–測試技術9

翻譯自: https://www.javacodegeeks.com/2011/12/why-you-should-write-unit-tests-testing.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/374439.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/374439.shtml
英文地址,請注明出處:http://en.pswp.cn/news/374439.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Git遷移 從SVN到Git

Migrating from SVN to Git 首先我們需要在Stach或者GitHub上新建一個Repository, 拿到它的URL。 接下來參照如下步驟 : At first we should create a new git repository at Stash and get the repository URL, and then follow below steps: 1. 切換到本地git工作目錄 chang…

C語言代碼規范(二)空格

一、逗號, 之后加空格 printf("error! score[%d] %d\n", i, score[i]); 二、分號; 之后加空格 for(i 0; i < student_num; i) 三、關系運算符<、<、>、>、、! 前后加空格 if( (score[i] > 0) && (score[i] < 100) ) 四、賦值運算符…

c++ 多重背包狀態轉移方程_動態規劃入門——詳解經典問題零一背包

本文始發于個人公眾號&#xff1a;TechFlow&#xff0c;原創不易&#xff0c;求個關注今天是周三算法與數據結構專題的第12篇文章&#xff0c;動態規劃之零一背包問題。在之前的文章當中&#xff0c;我們一起探討了二分、貪心、排序和搜索算法&#xff0c;今天我們來看另一個非…

Discuz! 的編碼規范

前言 本規范由編程原則組成&#xff0c;融合并提煉了開發人員長時間積累下來的成熟經驗&#xff0c;意在幫助形成良好一致的編程風格。適用范圍 如無特殊說明&#xff0c;以下規則要求完全適用于Discuz!項目&#xff0c;同時也可大部分適用于COMSENZ旗下其他PHP項目。標準化的重…

C語言代碼規范(三)if語句

一、整型變量與0比較 許多人為了一時之便&#xff0c;模仿布爾變量風格寫為如下代碼 if(value) {... }if(!value) {... } 應當用 或 ! 來與0比較 if(0 value) {... }if(0 ! value) {... } 二、當if內的語句是與常量進行比較時&#xff0c;常量為左值&#xff0c;變量為右…

6月24 面向對象的設計原則-----工廠模式和單列模式

工廠模式&#xff1a; 工廠模式就是專門負責將大量有共同接口的類實例化&#xff0c;而且不必事先知道每次是要實例化哪一個類的模式。它定義一個用于創建對象的接口&#xff0c;由子類決定實例化哪一個類。 工廠模式相當于創建實例對象的new&#xff0c;經常要根據類Class生成…

LeetCode Subsets

原題鏈接在這里&#xff1a;https://leetcode.com/problems/subsets/ 題目&#xff1a; Given a set of distinct integers, nums, return all possible subsets. Note: Elements in a subset must be in non-descending order.The solution set must not contain duplicate su…

使用ThreadPoolExecutor并行化獨立的單線程任務

Java SE 5.0中引入的任務執行框架是簡化多線程應用程序的設計和開發的巨大飛躍。 該框架提供了用于管理任務概念&#xff0c;管理線程生命周期及其執行策略的工具。 在此博客文章中&#xff0c;我們將描述該框架的功能&#xff0c;靈活性和簡單性&#xff0c;以展示一個簡單的用…

python定義一個圓_Python-矩形和圓形

原博文 2019-11-11 12:34 ? Exercise 15.1. 定義一個叫做Circle 類&#xff0c;類的屬性是圓心 (center) 和半徑 (radius) , 其中&#xff0c;圓心 (center) 是一個 Point 類&#xff0c;而半徑 (radius) 是一個數字。 實例化一個圓心 (center) 為 (150, 100) &#xff0c;半…

C語言代碼規范(四)命名規則

一、宏定義全部字母大寫&#xff0c;單詞間下劃線間隔 #define FLASH_PAGE_SIZE 256 #define FLASH_SECTOR_SIZE (4 * 1024) #define FLASH_BLOCK_SIZE (64 * 1024) #define FLASH_SIZE (16 * 1024 * 1024) 二、const修飾的常量全部字母大寫&#xff0c;單詞間…

Forbidden You don't have permission to access / on this server PHP

Forbidden You dont have permission to access / on this server PHP 在新安裝的谷歌游覽器里&#xff0c;打不了PHP網站了&#xff0c;錯誤顯示&#xff1a; Forbidden You dont have permission to access / on this server. 原因還是配置權限問題 解決辦法&#xff1a; wa…

Spring 3.1和JPA的持久層

1.概述 本教程顯示了如何使用Hibernate作為持久性提供程序使用JPA設置Spring 。 有關使用基于Java的配置和項目的基本Maven pom設置Spring上下文的分步介紹&#xff0c;請參閱本文 。 2. Java的JPA Spring配置 要在Spring項目中使用JPA&#xff0c; 需要設置EntityManager 。…

150928錯誤認識

1. $arr array(); foreach ($re as $k>$v){  $arr[] $v[updatetime];} $arr的返回結果為&#xff1a; Array ([0] > 2014-09[1] > 2015-04[2] > 2015-09 )$arr array(); foreach ($re as $k>$v){  $arr[$k] $v[updatetime];} $arr的返回結果為&#xff…

STM32F1筆記(一)GPIO輸出

GPIO&#xff1a;General Purpose Input Output &#xff08;通用輸入/輸出&#xff09;。 GPIO最經典應用&#xff1a;LED燈。 先看電路。聲明&#xff1a;參考正點原子戰艦開發板。 與LED串聯的電阻稱為限流電阻。 限流電阻計算公式&#xff1a;R(U-LED壓降)/20ma。 U為LE…

dataframe轉化為array_【Python專欄】12 種高效 Numpy 和 Pandas 函數為你加速分析

來源&#xff1a;機器之心編譯&#xff1a;Jamin、杜偉、張倩我們都知道&#xff0c;Numpy 是 Python 環境下的擴展程序庫&#xff0c;支持大量的維度數組和矩陣運算&#xff1b;Pandas 也是 Python 環境下的數據操作和分析軟件包&#xff0c;以及強大的數據分析庫。二者在日常…

具有GlassFish和一致性的高性能JPA –第1部分

您以前聽說過連貫性嗎&#xff1f; 大概是。 它是那些著名的內存網格解決方案之一&#xff0c;該解決方案承諾了超快的數據訪問速度和對經常使用的數據的無限空間。 一些眾所周知的競爭對手是Infinispan &#xff0c; Memcached和Terracotta Ehcache 。 它們都很棒&#xff0c;…

如何在自己的代碼中實現分享視頻文件或者是圖片文件到微信 QQ微博 新浪微博等!!!...

首先在文檔第一句我先自嘲下 &#xff0c; 我是大傻逼&#xff0c; 弄了兩天微信是視頻分享&#xff0c;一直被說為啥跟系統的相冊分享的不一樣&#xff0c;尼瑪&#xff01;&#xff01;&#xff01; 這里來說正文&#xff0c;我這里不像多少太多&#xff0c;大家都是程序猿&a…

sql 數據庫中用創建好的視圖修改表數據

只要滿足下列條件&#xff0c;即可通過視圖修改基礎基表的數據&#xff1a; 1、任何修改&#xff08;包括 UPDATE、INSERT 和 DELETE 語句&#xff09;都只能引用一個基表的列。 2、視圖中被修改的列必須直接引用表列中的基礎數據。不能通過任何其他方式對這些列進行派生&#…

boost原理與sklearn源碼_機器學習sklearn系列之決策樹

一、 Sklearn庫 Scikit learn 也簡稱 sklearn, 自2007年發布以來&#xff0c;scikit-learn已經成為Python重要的機器學習庫了。支持包括分類、回歸、降維和聚類四大機器學習算法。還包含了特征提取、數據處理和模型評估三大模塊。sklearn是Scipy的擴展&#xff0c;建立在NumPy和…

STM32F1筆記(二)GPIO輸入

STM32 GPIO輸入的經典應用是按鍵。 先看電路。聲明&#xff1a;參考正點原子戰艦開發板。 在這里可以看到&#xff0c;KEY_UP按鍵是高電平有效的&#xff0c;即當按下該按鍵時&#xff0c;GPIO讀到高電平。 KEY0/1/2是低電平有效的&#xff0c;即當按下該按鍵時&#xff0c;G…