小架構step系列12:單元測試

1 概述

測試的種類很多:單元測試、集成測試、系統測試等,程序員寫代碼進行測試的可以稱為白盒測試,單元測試和集成測試都可以進行白盒測試,可以理解為單元測試是對某個類的某個方法進行測試,集成測試則是測試一連串的方法。測試代碼也是代碼,即使要求測試代碼邏輯簡單,但寫單元測試代碼也是要花時間的,維護也是要花時間的,所以在寫測試代碼上也要有些平衡。單元測試的顆粒度比較小,最接近方法的業務邏輯,應該詳盡的測試;而集成測試牽扯的方法比較多,就比較復雜,就挑重點的地方做測試。本文先了解單元測試。

2 單元測試

2.1 業務邏輯測試

在DDD里有一種思想,就是要把業務邏輯分離出來,這部分業務邏輯是業務的核心,應該是公司的核心價值所在,所以應該進行詳盡測試。測試要做得多,成本就要低,所以這塊的測試主要集中在邏輯方面,業務邏輯的代碼也要寫成函數的形式,也就是給予一定的參數,就會反饋相同的結果。這塊代碼不應該跟數據庫、中間件等扯上關系,否則就很難做到輕量化。

有不少業務邏輯代碼是比較簡單的,不一定會按DDD的模式進行文件或者目錄分離,但也要在類或者方法層面做到把業務邏輯分離,以便對這些業務進行良好的單元測試。這種測試僅需要對一些類進行mock,然后就跟測試一個有參數有返回值的方法那樣簡單。這就要求涉及到數據庫或者中間件等外部資源的操作,都應該接口化,這樣就比較容易進行mock。

2.2 測試例子

假設有個創建組成員的功能,為這個業務邏輯創建一個服務接口GroupMemberCreator和服務類GroupMemberCreatorImpl,里面有個方法create(GroupMember member),用于編寫創建成員的業務邏輯,有部分業務會封裝到業務對象GroupMember中;這個方法里需要把數據存儲到數據庫,則通過GroupMemberRepository提供save()接口把數據存儲到數據庫中,該接口只有數據庫相關操作,而沒有業務邏輯。

// 把業務邏輯放到GroupMember和GroupMemberCreatorImpl里
public class GroupMember {private Long groupId;private String name;public GroupMember(Long groupId, String name) {// 做業務邏輯:校驗參數并組裝GroupMember信息this.groupId = groupShouldExist(groupId);this.name = memberNameShouldNotExist(name);}public Long getGroupId() {return groupId;}public String getName() {return name;}private Long groupShouldExist(Long groupId) {// 校驗組存在,否則拋異常return groupId;}private String memberNameShouldNotExist(String name) {// 校驗成員是否已經存在,否則拋異常return name;}
}public interface GroupMemberCreator {GroupMember create(Long groupId, String memberName);
}
@Service
public class GroupMemberCreatorImpl implements GroupMemberCreator {private GroupMemberRepository repository;@Autowiredpublic GroupMemberCreatorImpl(GroupMemberRepository repository) {this.repository = repository;}@Overridepublic GroupMember create(Long groupId, String memberName) {GroupMember member = new GroupMember(groupId, memberName);return repository.save(member);}
}// Repository只有數據庫相關操作,無業務邏輯
public interface GroupMemberRepository {GroupMember save(GroupMember member);
}
@Repository
public class GroupMemberReposistoryImpl implements GroupMemberRepository {@Overridepublic GroupMember save(GroupMember member) {// 調相關DAO操作數據庫return member;}
}// 測試用例例子
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import static org.mockito.ArgumentMatchers.any;
public class GroupMemberCreatorImplTest {@Testpublic void create_new_member() {Long groupId = 1L;String name = "Joy";// 1. mock對象// 調repository.save()的時候,返回一個mock對象,不真正調用數據庫GroupMember mockMember = new GroupMember(groupId, name);GroupMemberRepository repository = Mockito.mock(GroupMemberRepository.class);Mockito.when(repository.save(any())).thenReturn(mockMember);// 2. 測試代碼邏輯GroupMemberCreatorImpl creator = new GroupMemberCreatorImpl(repository);GroupMember member = creator.create(groupId, name);// 3. 斷言(Assertion)Assertions.assertThat(member).isNotNull();Assertions.assertThat(member.getGroupId()).isEqualTo(groupId);Assertions.assertThat(member.getName()).isEqualTo(name);}
}

從上面測試用例來看,測試的三個步驟:一是用mockito進行mock對象,指定mock對象執行方法的返回值(可根據參數返回);二是調業務邏輯代碼進行測試;三是對測試結果進行斷言。

通過測試可以反過來思考方法應該如何設計,其規則大概是看輸入輸出,也就是有什么輸入就預期響應的輸出,要注意的是方法返回值不一定是唯一的輸出,如果方法內有數據庫等操作,寫入數據庫的內容也算一個輸出,這個時候可以考慮把數據庫的輸入反饋到方法返回值當中,總之是要驗證指定的輸入有指定的輸出。

注:調save()保存數據嚴格來說也不算業務,其只與技術有關,可以進一步從業務邏輯中剝離出來。上面主要是方便說明mock一個對象的例子。

2.3 文檔

2.3.1 Mockito

Mockito還是需要學習一下具體的用法的,重點可以先了解如何匹配參數,然后如何指定返回值(含拋異常)等,再高級的用法則可以用到再查找文檔。

Mockito各個版本的文檔列表:https://javadoc.io/doc/org.mockito/mockito-core/latest/org.mockito/org/mockito/Mockito.html

Mockito-4.5.1的詳細文檔:https://javadoc.io/doc/org.mockito/mockito-core/4.5.1/org/mockito/Mockito.html

2.3.2 AssertJ

AssertJ則通過方法名稱就大概了解其用法了,重點可以了解一下在拋異常的場景如何進行斷言。

文檔:https://assertj.github.io/doc/

3 架構一小步

規范:把業務規則抽離出來,不依賴數據庫和中間件進行詳細的單元測試。

規范:編寫可測試的代碼,如果測試代碼有邏輯則反向說明代碼可測試性不足。

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

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

相關文章

SpringBoot3-Flowable7初體驗

目錄簡介準備JDKMySQLflowable-ui創建流程圖要注意的地方編碼依賴和配置控制器實體Flowable任務處理類驗證啟動程序調用接口本文源碼參考簡介 Flowable是一個輕量的Java業務流程引擎,用于實現業務流程的管理和自動化。相較于老牌的Activiti做了一些改進和擴展&…

phpMyAdmin:一款經典的MySQL在線管理工具又回來了

phpMyAdmin 是一個免費開源、基于 Web 的 MySQL/MariaDB 數據庫管理和開發工具。它提供了一個直觀的圖形用戶界面,使得我們無需精通復雜的 SQL 命令也能執行大多數數據庫管理任務。 phpMyAdmin 項目曾經暫停將近兩年,不過 2025 年又開始發布新版本了。 …

存儲服務一NFS文件存儲概述

前言: 網絡文件系統(Network File System,NFS)誕生于1984年,由Sun Microsystems首創,旨在解決異構系統間的文件共享需求。作為一種基于客戶端-服務器架構的分布式文件協議,NFS允許遠程主機通過T…

libimagequant 在 mac 平臺編譯雙架構

在 macOS 上編譯 libimagequant 的雙架構(aarch64 x86_64)通用二進制庫,以下是完整步驟:??1. 準備 Rust 工具鏈?? # 安裝兩個目標平臺 rustup target add aarch64-apple-darwin x86_64-apple-darwin# 確認安裝成功 rustup ta…

暑期自學嵌入式——Day01(C語言階段)

點關注不迷路喲。你的點贊、收藏,一鍵三連,是我持續更新的動力喲!!! 主頁: 一位搞嵌入式的 genius-CSDN博客https://blog.csdn.net/m0_73589512?spm1011.2682.3001.5343感悟: 今天我認為最重…

Flutter基礎(前端教程⑧-數據模型)

這個示例展示了如何創建數據模型、解析 JSON 數據,以及在 UI 中使用這些數據:import package:flutter/material.dart; import dart:convert;void main() {// 示例:手動創建User對象final user User(id: 1,name: 張三,age: 25,email: zhangsa…

SSRF10 各種限制繞過之30x跳轉繞過協議限制

ssrf漏洞在廠商的處理下可能進行一些特殊處理導致我們無法直接利用漏洞 有以下四種: 1.ip地址限制繞過 2.域名限制繞過 3.30x跳轉繞過域名限制 4.DNS rebinding繞過內網ip限制 本章我們講30x跳轉繞過域名限制 30x跳轉繞過域名限制 之前我們使用ssrf漏洞時可以…

DNS解析過程和nmap端口掃描

目錄 DNS解析流程: nmap端口掃描 指定掃描方式 TCP全連接掃描 -sT SYN半連接掃描 -sS -sT和 -sS的區別 Linux提權 利用好谷歌語法查找敏感信息 如果自己搭建了網站文件要放在phpstudy_pro\WWW下。 如果想要使用域名訪問網站,需要在phpstudy_pro…

【基于開源大模型(如deepseek)開發應用及其發展趨勢的一點思考】

1. 開源大模型技術發展現狀1.1 DeepSeek等主流開源大模型的技術特性分析 DeepSeek作為當前最具代表性的開源大模型之一,其技術架構具有多項創新特性。該模型采用混合專家架構(MoE),通過將視覺編碼分離為"理解"和"生成"兩條路徑&…

java8 ConcurrentHashMap 桶級別鎖實現機制

Java 8 ConcurrentHashMap 桶級別鎖實現機制 Java 8 中的 ConcurrentHashMap 拋棄了分段鎖設計,采用了更細粒度的桶級別鎖(bucket-level locking)實現,這是其并發性能提升的關鍵。下面詳細解析其實現原理: 1. 基本實現…

Python正則表達式實戰指南

一 正則表達式庫正則表達式是文本處理中不可或缺的強大工具,Python通過re模塊提供了完整的正則表達式支持。本文將詳細介紹re模塊中最常用的match()、search()和findall()函數,以及貪婪模式與非貪婪模式的區別,幫助讀者掌握Python中正則表達式…

使用球體模型模擬相機成像:地面與天空的可見性判斷與紋理映射

在傳統相機模擬中,地面通常被建模為一個平面(Plane),這在低空場景下是合理的。但在更大視場范圍或遠距觀察時,地球的曲率不可忽視。因此,我們需要將地面模型從平面升級為球體,并基于球面與光線的…

Agent自動化與代碼智能

核心問題: 現在很多團隊做AI系統有個大毛病:只顧追求“高大上”的新技術(尤其是AI Agent),不管實際業務需不需要。 結果系統搞得又貴、又復雜、還容易出錯。大家被“Agent”這個概念搞暈了:到底啥時候用簡單…

SQL141 試卷完成數同比2020年的增長率及排名變化

SQL141 試卷完成數同比2020年的增長率及排名變化 withtemp as (selectexam_id,tag,date(submit_time) as submit_timefromexamination_infoleft join exam_record using (exam_id)wheresubmit_time is not null),2021_temp as (selecttag,count(*) as exam_cnt_21,rank() over…

C語言<數據結構-單鏈表>

鏈表是一種常見且重要的數據結構,在 C 語言中,它通過指針將一系列的節點連接起來,每個節點可以存儲不同類型的數據。相比數組,鏈表在插入和刪除元素時不需要移動大量數據,具有更好的靈活性,尤其適合處理動態…

archive/tar: unknown file mode ?rwxr-xr-x

這個是我在docker build報錯的,這是一個node.js項目。我猜你也是一個node.js下的項目,或者前端項目。 解決方法: .dockerignore里面寫一下node_modules就行了。 未能解決:archive/tar:未知文件模式?rwxr-…

【前端】ikun-markdown: 純js實現markdown到富文本html的轉換庫

文章目錄背景界面當前支持的 Markdown 語法不支持的Markdown 語法代碼節選背景 出于興趣,我使用js實現了一個 markdown語法 -> ast語法樹 -> html富文本的庫, 其速度應當慢于正則實現的同類js庫, 但是語法擴展性更好, 嵌套列表處理起來更方便. 界面 基于此js實現vue組…

【echarts踩坑記錄】為什么第二個Y軸最大值不整潔

目錄問題復現示意圖:解決方法有以下幾種:1. 在y軸配置中手動設置max屬性:2. 使用ECharts提供的坐標軸標簽格式化功能:🚀寫在最后問題復現示意圖: 今天在用echarts圖表的時候,出現了一個小問題。…

Duplicate cleaner pro 的使用技巧

Duplicate cleaner pro 的使用技巧前言文件去重基本介紹經驗之談目錄結構修改盤符起因方法手動分配方法?數據修改方法安裝sqlite-web修改數據庫GPU加速安裝驅動獲取驅動和硬件信息安裝CUDA配置環境變量&#xff08;如果是自定義安裝&#xff09;創建程序<1>獲取參數和命…

數字孿生技術引領UI前端設計新趨勢:增強現實與虛擬現實的融合應用

hello寶子們...我們是艾斯視覺擅長ui設計和前端數字孿生、大數據、三維建模、三維動畫10年經驗!希望我的分享能幫助到您!如需幫助可以評論關注私信我們一起探討!致敬感謝感恩!一、引言&#xff1a;AR 與 VR 的 “割裂” 與數字孿生的 “融合” 契機增強現實&#xff08;AR&…