TDD(測試驅動開發)?

01、前言

很早之前,曾在網絡上見到過 TDD 這 3 個大寫的英文字母,它是 Test Driven Development 這三個單詞的縮寫,也就是“測試驅動開發”的意思——聽起來很不錯的一種理念。

其理念主要是確保兩件事:

  • 確保所有的需求都能被照顧到
  • 在代碼不斷增加和重構的過程中,可以檢查所有的功能是否正確

但后來很長一段時間里,都沒再聽過 TDD 的消息。有人說,TDD 已經死了,給出的意見如下:

1)通常來說,開發人員不應該在沒有失敗的測試用例下編寫代碼——這似乎是合理的,但是它可能導致過度測試。例如,為了保證一行生產代碼的正確性,你不由得寫了 4 行測試代碼,這意味著一旦這一行生產代碼需要修改,你也得修改那 4 行測試代碼。

2)為了遵循 TDD 而寫的代碼,容易進入一個誤區:代碼是為了滿足測試用的,而忽略了實際需求。

02、TDD 到底是什么?

不管 TDD 到底死了沒有,先讓我們來回顧一下 TDD 到底是什么。

TDD 的基本思想就是在開發功能代碼之前,先編寫測試代碼。也就是說在明確要開發某個功能后,首先思考如何對這個功能進行測試,并完成測試代碼的編寫,然后編寫相關的代碼滿足這些測試用例。然后循環進行添加其他功能,直到完成全部功能的開發

TDD 的基本過程可以拆解為以下 6 個步驟:

1) 分析需求,把需求拆分為具體的任務。

2) 從任務列表中取出一個任務,并對其編寫測試用例。

3) 由于沒有實際的功能代碼,測試代碼不大可能會通過(紅)。

4) 編寫對應的功能代碼,盡快讓測試代碼通過(綠)。

5) 對代碼進行重構,并保證測試通過(重構)。

6) 重復以上步驟。

可以用下圖來表示上述過程。

?

03、TDD 的實踐過程

通常情況下,我們都習慣在需求分析完成之后,盡快地投入功能代碼的編寫工作中,之后再去調用和測試。

而 TDD 則不同,它假設我們已經有了一個“測試用戶”了,它是功能代碼的第一個使用者,盡管功能代碼還不太完善。

當我們站在“測試用戶”的角度去寫測試代碼的時候,我們要考慮的是,這個“測試用戶”該如何使用功能代碼呢?是通過一個類直接調用方法呢(靜態方法),還是構建類的實例去調用方法呢(實例方法)?這個方法如何傳參呢?方法如何命名呢?方法有返回值嗎?

有了測試代碼后,我們開始編寫功能代碼,并且要以最快地速度讓測試由“紅”變為“綠”,可能此時的功能代碼很不優雅,不過沒關系

當測試通過以后,我們就可以放心大膽的對功能代碼進行“重構”了——優化原來比較丑陋、臃腫、性能偏差的代碼。

接下來,假設我們接到了一個開發需求:

汪汪隊要到小鎮冒險島進行表演,門票為 99 元,冒險島上唯一的一個程序員王二需要開發一款可以計算門票收入的小程序。

按照 TDD 的流程,王二需要先使用 Junit 編寫一個簡單的測試用例,測試預期是:銷售一張門票的收入是 99 元。

public?class?TicketTest?{private?Ticket?ticket;@Beforepublic?void?setUp()?throws?Exception?{ticket?=?new?Ticket();}@Testpublic?void?test()?{BigDecimal?total?=?new?BigDecimal("99");assertEquals(total,?ticket.sale(1));}}

為了便于編譯能夠順利通過,王二需要一個簡單的 Ticket 類:

public?class?Ticket?{public?BigDecimal?sale(int?count)?{return?BigDecimal.ZERO;}}

測試用例運行結果如下圖所示,紅色表示測試沒有通過:預期結果是 99,實際結果是 0。

那接下來,王二需要快速讓測試通過,Ticket.sale()?方法修改后的結果如下:

public?class?Ticket?{public?BigDecimal?sale(int?count)?{if?(count?==?1)?{return?new?BigDecimal("99");}return?BigDecimal.ZERO;}}

再運行一下測試用例,結果如下圖所示,綠色表示測試通過了:預期結果是 99,實際結果是 99。

綠了,綠了,測試通過了,到了該重構功能代碼的時候了。99 元是個魔法數字,至少應該聲明成常量,對吧?

public?class?Ticket?{private?final?static?int?PRICE?=?99;public?BigDecimal?sale(int?count)?{if?(count?==?1)?{return?new?BigDecimal(PRICE);}return?BigDecimal.ZERO;}}

重構完后再運行一下測試用例,確保測試通過的情況下,再增加幾個測試用例,比如說門票銷量為負數、零甚至一千的情況。

public?class?TicketTest?{private?Ticket?ticket;@Beforepublic?void?setUp()?throws?Exception?{ticket?=?new?Ticket();}@Testpublic?void?testOne()?{BigDecimal?total?=?new?BigDecimal("99");assertEquals(total,?ticket.sale(1));}@Test(expected=IllegalArgumentException.class)public?void?testNegative()?{ticket.sale(-1);}@Testpublic?void?testZero()?{assertEquals(BigDecimal.ZERO,?ticket.sale(0));}@Testpublic?void?test1000()?{assertEquals(new?BigDecimal(99000),?ticket.sale(1000));}}

銷量為負數的時候,王二希望功能代碼能夠拋出異常;銷量為零的時候,功能代碼的計算結果應該為零;銷量為一千的時候,計算結果應該為 99000。

有兩個測試用例沒有通過,那么王二需要繼續修改功能代碼,調整如下:

public?class?Ticket?{private?final?static?int?PRICE?=?99;public?BigDecimal?sale(int?count)?{if?(count?<?0)?{throw?new?IllegalArgumentException("銷量不能為負數");}if?(count?==?0)?{return?BigDecimal.ZERO;}if?(count?==?1)?{return?new?BigDecimal(PRICE);}return?new?BigDecimal(PRICE?*?count);}}

再運行一下測試用例,發現都通過了。又到了重構的時候了,銷量為零、或者大于等于一的時候,代碼可以合并,于是重構結果如下:

public?class?Ticket?{private?final?static?int?PRICE?=?99;public?BigDecimal?sale(int?count)?{if?(count?<?0)?{throw?new?IllegalArgumentException("銷量不能為負數");}return?new?BigDecimal(PRICE?*?count);}}

重構結束后,再運行測試用例,確保重構后的代碼依然可用。

04、最后

從上面的實踐過程可以得出如下結論:

TDD 想要做的就是讓我們對自己的代碼充滿信心,因為我們可以通過測試代碼來判斷這段代碼是否正確無誤。

也就是說,TDD 流程比較關鍵的一環在于如何寫出有效的測試代碼,這里有 4 個原則可以參考:

1)測試過程應該盡量模擬正常使用的過程。

2)應該盡量做到分支覆蓋。

3)測試數據應該盡量包括真實數據,以及邊界數據。

4)測試語句和測試數據應該盡量簡單,容易理解。

注意,這 4 個原則不僅適用于 TDD,同樣適用于任何流程下的單元測試。

最后,我想說的是,不管 TDD 有沒有死,TDD 都不是銀彈,不可能適合所有的場景,但這不應該成為我們拒絕它的理由。

【B站最全最易學】十年大佬終于將測試開發路線整理出來了,小白一學就會,拿走不謝,允許白嫖!!

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

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

相關文章

macOS Ventura 13.5.1(22G90)發布(附黑/白蘋果系統鏡像地址)

系統鏡像下載&#xff1a;百度&#xff1a;黑果魏叔 系統介紹 黑果魏叔 8 月 18 日消息&#xff0c;蘋果今日向 Mac 電腦用戶推送了 macOS 13.5.1 更新&#xff08;內部版本號&#xff1a;22G90&#xff09;&#xff0c;本次更新距離上次發布隔了 24 天。 本次更新重點修復了…

Redis 緩存過期及刪除

一、Redis緩存過期策略 物理內存達到上限后&#xff0c;像磁盤空間申請虛擬內存(硬盤與內存的swap),甚至崩潰。 內存與硬盤交換 (swap) 虛擬內存&#xff0c;頻繁I0 性能急劇下降&#xff0c;會造成redis內存急劇下降&#xff1b; 一般設置物理內存的3/4&#xff0c;在redis…

內存不足V4L2 申請DMC緩存報錯問題

當內存不足時,V4L2可能存在申請DMA緩存報錯,如下日志: 13:36:54:125 [15070.640862] rkcifhw fdfe0000.rkcif: swiotlb buffer is full (sz: 1843200 bytes) 13:36:54:125 [15070.640891] rkcifhw fdfe0000.rkcif: swiotlb: coherent allocation failed, size=1843200 13:3…

超分辨率地震速度模型

文獻分享 1. Multitask Learning for Super-Resolution 原題目&#xff1a;Multitask Learning for Super-Resolution of Seismic Velocity Model 全波形反演&#xff08;FWI&#xff09;是估算地下速度模型的強大工具。與傳統反演策略相比&#xff0c;FWI充分利用了地震波的…

typedef

t y p e d e f typedef typedef 聲明&#xff0c;簡稱typedef&#xff0c;是創建現有類型的新名字。 比如&#xff1a; #include <bits/stdc.h> using namespace std; typedef long long ll; int main() {ll n;scanf("%lld",&n);printf("%lld"…

C++ 面向對象三大特性——多態

?<1>主頁&#xff1a;我的代碼愛吃辣 &#x1f4c3;<2>知識講解&#xff1a;C 繼承 ??<3>開發環境&#xff1a;Visual Studio 2022 &#x1f4ac;<4>前言&#xff1a;面向對象三大特性的&#xff0c;封裝&#xff0c;繼承&#xff0c;多態&#xff…

30W IP網絡有源音箱 校園廣播音箱

SV-7042XT是深圳銳科達電子有限公司的一款2.0聲道壁掛式網絡有源音箱&#xff0c;具有10/100M以太網接口&#xff0c;可將網絡音源通過自帶的功放和喇叭輸出播放&#xff0c;可達到功率30W。同時它可以外接一個30W的無源副音箱&#xff0c;用在面積較大的場所。5寸進口全頻低音…

RNN模型簡單理解和CNN區別

目錄 神經網絡&#xff1a;水平方向延伸&#xff0c;數據不具有關聯性 ? RNN&#xff1a;在神經網絡的基礎上加上了時間順序&#xff0c;語義理解 ?RNN: 訓練中采用梯度下降&#xff0c;反向傳播 ? 長短期記憶模型 ?輸出關系&#xff1a;1 toN&#xff0c;N to N 單入…

Spring三級緩存

目錄 循環依賴問題 三級緩存 三級緩存創建Bean的流程&#xff08;解決循環依賴問題&#xff09; 三級緩存的局限性 Spring的三級緩存是為了解決單例Bean的循環依賴問題而存在的。 循環依賴問題 簡單來說就是A依賴B&#xff0c;而B又依賴A。即創建A的時候&#xff0c;需要先…

【HarmonyOS】【DevEco Studio】ohpm安裝失敗該如何解決?

【關鍵詞】 HarmonyOS、DevEco Studio、ohpm安裝失敗 【問題背景及解決方案】 最近遇到很多DevEco Studio安裝ohpm失敗的問題&#xff0c;下面給大家介紹幾種出現的問題以及解決方案&#xff1a; 1、ohpm not set up&#xff0c;報錯截圖如下&#xff1a; ? 解決方案&…

一百六十、Kettle——Linux上安裝的Kettle9.2.0連接Hive3.1.2

一、目標 Kettle9.2.0在Linux上安裝好后&#xff0c;需要與Hive3.1.2數據庫建立連接 之前已經在本地上用kettle9.2.0連上Hive3.1.2 二、各工具版本 &#xff08;一&#xff09;kettle9.2.0 kettle9.2.0安裝包網盤鏈接 鏈接&#xff1a;https://pan.baidu.com/s/15Zq9w…

C++中class嵌套時構造函數,析構函數調用的順序

#include<iostream> using namespace std; class Phone { public:Phone(string pname){m_pnamepname;cout<<"phone的構造函數調用"<<endl;}~Phone(){cout<<"Phone的析構函數調用"<<endl;}string m_pname; }; class Person {…

網安周報|Monti Ransomware團伙推出了一個新的Linux加密器

Monti Ransomware團伙推出了一個新的Linux加密器 經過兩個月的休息&#xff0c;Monti 勒索軟件運營商帶著新的 Linux 版本的加密器返回。該變體被用于針對政府和法律部門組織的攻擊。研究人員注意到兩個團伙的TTP之間有多個相似之處&#xff0c;Monti運營商還基于Conti泄露的源…

2023 Robocom 游記+題解

Robocom賽前一天熬夜打了一場edu,全程瞇瞇眼&#xff0c;三題滾粗了&#xff0c;前三題花了一小時才寫完&#xff0c;第四題寫了一小時也沒寫明白&#xff0c;好像預示著Robocom的結局&#xff1f; 早上七點醒了&#xff0c;感覺自己渾身無力&#xff0c;想睡覺但是又睡不著的…

AutoSAR配置與實踐(基礎篇)3.3 BSW的通信功能

傳送門 -> AUTOSAR配置與實踐總目錄 AutoSAR配置與實踐&#xff08;基礎篇&#xff09;3.3 BSW的通信功能 一、收發過程概覽1.1 發送過程概覽1.2 接收過程概覽 二、BSW的通信功能模塊組成三、收發過程解析3.1 發送過程3.2 發送后的結果確認3.3 接收過程 一、收發過程概覽 1…

Airbnb開源數據可視化工具Visx

一、什么是visx visx 是用于 React 的富有表現力的底層可視化組件集合,結合了 d3 的強大功能來生成可視化,以及 React 更新 DOM 的諸多優勢。 在 Airbnb 內部,visx 的目標是統一整個公司的可視化堆棧,在此過程中,創建了 visx 項目,從而有效的將 D3 的強大功能與 React …

內核調試之devmem直接讀寫寄存器

今天分享一個內核調試實用工具——devmem。 相信很多做底層驅動的人都會經常用到。 什么是devmem&#xff1f; 在Linux系統&#xff0c;如果我們想要訪問某個寄存器&#xff0c;就需要寫一個驅動程序&#xff0c;在驅動中映射寄存器地址&#xff0c;轉為虛擬地址后就可以訪問…

windows電腦系統自帶的畫圖工具如何實現自由拼圖

1.首先選中你要拼接的第一張圖片&#xff0c;右鍵選著編輯&#xff0c;會自動打開自帶的畫圖工具 然后就是打開第一張圖片&#xff0c;如下圖所示 接著就是將畫布托大&#xff0c;如下圖所示。 然后點擊選擇&#xff0c;選擇下面的空白區域&#xff0c;選著區域的范圍要比準備拼…

05-微信小程序常用組件-表單組件

05-微信小程序常用組件-表單組件 文章目錄 表單組件button 按鈕案例代碼 form 表單案例代碼 image 圖片支持長按識別的碼案例代碼 微信小程序包含了六大組件&#xff1a; 視圖容器、 基礎內容、 導航、 表單、 互動和 導航。這些組件可以通過WXML和WXSS進行布局和樣式設…

jQuery第一次接觸

jQuery是一個輕量級js庫 1.下載jquery庫&#xff0c;網址Download jQuery | jQuery npm i jquery 2.還可以從cdn中載入jquery <script src"https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"> 3.j代表js&#xff0c;query代表查詢&#xff0c;jQu…