欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Springboot單元測試編寫實踐

 更新時間:2023年11月03日 08:26:17   作者:斜月86  
在日常的開發(fā)過程中,為了提高代碼的可靠性和健壯性,同時也是檢測代碼的質(zhì)量,減少測試環(huán)節(jié)的問題,會對完成的業(yè)務功能代碼編寫單元測試,在本文中,將分享一些單元測試的實踐和心得,需要的朋友可以參考下

1 前言

在日常的開發(fā)過程中,為了提高代碼的可靠性和健壯性,同時也是檢測代碼的質(zhì)量,減少測試環(huán)節(jié)的問題,會對完成的業(yè)務功能代碼編寫單元測試。有時間單元測試的覆蓋率也是工作的一部分。作者最近被安排了一項艱巨的單測任務,在本文中,將分享一些單元測試的實踐和心得。

2 生成單元測試

通常情況下,單元測試都是使用 junit 編寫的,但是這種方式會真實的調(diào)用數(shù)據(jù),如何優(yōu)雅的實現(xiàn)單元測試是一個問題。這里使用的是 powermock 來實現(xiàn)測試用例的編寫。引入 powermock 依賴如下所示:

<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4</artifactId>
    <version>2.0.9</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito2</artifactId>
    <version>2.0.9</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>3.11.2</version>
    <scope>test</scope>
</dependency>

在編寫單元測試之前,需要在 idea 中安裝一個 squaretest 插件,在需要編寫單元測試的類中,通過右鍵 generate -> generate Test 可以打開一個面板,可以選擇一個模板,就可以在test中生成具體的單元測試類。

可以這樣說,有了這個便捷的插件助力,對于簡單的場景,就可以完全覆蓋其業(yè)務功能。

3 單測重點

雖然說通過 squaretest 可以實現(xiàn)大部分代碼的編寫,但是有一些場景還不能那么智能的實現(xiàn),需要編寫代碼實現(xiàn)功能。如下圖所示,除了第三個單測點外,其它的都是不常見的類型。

3.1 mock static 方法

static 方法的 mock 需要使用到 mockStatic 方法,具體的操作如下所示:

// 第一步需要在測試類上添加類名稱
@PrepareForTest(value = {類名.class})
// 第二步需要使用 mockStatic 聲明,然后進行mock 操作
mockStatic(類名.class)
when(類名.static方法()).thenReturn(mock結果);

static 方法的 mock, 適用于需要加載系統(tǒng)配置或者初始化文件的場景,通過 mock 類的 static 方法,即可獲取相應的返回值,避免類的初始化導致單元測試報錯。

3.2 mock 分布式鎖

分布式鎖的 mock, 需要考慮的方面比較多,首先是根據(jù) redisclient 獲取分布式鎖,然后調(diào)用 tryLock 并等待加鎖的返回信息,這里其實是需要兩個 mock, 但是 getLock 返回的 RLock 是一個接口,不能使用創(chuàng)建新類的方式來實現(xiàn),這里就需要使用 mock() 來創(chuàng)建一個 RLock, 然后在其基礎上進行 mock , 由此可以實現(xiàn)兩層的 mock。這里需要說明的是,分布式鎖使用的 Redisson 來實現(xiàn)的。

// mock RLock
RLock mock = mock(RLock.class);
// mock tryLock 和 getLock 兩個方法
when(mock.tryLock(anyLong(), eq(TimeUnit.MINUTES))).thenReturn(Boolean.TRUE);
when(mockRedisUtils.getLock(anyString())).thenReturn(mock);

3.3 mock spring 中的 bean

在單元測試中常用的 mock 即測試類中注入的 service、business、mapper 等內(nèi)容,通過 mock 所涉及的方法,以期得到對應的返回值繼續(xù)業(yè)務流程的繼續(xù)。 when 和 thenReturn 需要組合使用,根據(jù)傳入的方法參數(shù)返回預期值。方法的入?yún)⒖梢允?any(), any(類.class), anyString(), anyInt(), anyLong() 等,但需要注意的是傳入的參數(shù)不能為 null,如果需要精確匹配,則需要使用 eq()。此外, thenReturn 的返回值可以有多個,支持鏈式調(diào)用,如果返回值有多個,則表示第一次調(diào)用方法返回第一個值,第二次調(diào)用方法返回第二個值,以此類推。另外,還有模擬方法調(diào)用發(fā)送異常的場景,則使用 doThrow 來返回對應的異常。

// mock 業(yè)務查詢和操作
when(mockMapper.selectByUserId(eq("123"))).thenReturn(user);
when(mockMapper.selectByUserId(anyString())).thenReturn(user, user, user);
when(mockMapper.selectByUserId(anyString())).thenReturn(user).thenReturn(user).thenReturn(user);
when(mockMapper.updateById(any(User.class))).thenReturn(1);
// 模擬調(diào)用拋出異常
doThrow(RuntimeException.class).when(mockUserMapper).selectByUserId(anyString());

3.4 mock redis template 操作

對于 redis 的操作,其實和分布式鎖的操作類似,以操作字符串類型為例,需要先獲取一個 opsForValue 而后來進行操作 redisTemplate.opsForValue().方法,由于 ValueOperations 也是一個接口,所以需要使用 mock 來獲取一個操作對象,基于此在進行 mock 所涉及的方法。

ValueOperations operations = mock(ValueOperations.class);
when(operations.increment(anyString())).thenReturn(230L);
when(redisTemplate.opsForValue()).thenReturn(operations);

3.5 mock 事務 transactionTemplate

事務的操作是在特殊業(yè)務場景下才會用到,這里的只是借此場景來說明如何對匿名類中的方法進行單測。默認情況下生成的測試代碼是不具備這種能力的,需要使用 thenAnswer 來處理。以下列舉了兩種方式,一種是匿名類的方式,一種是 lambda 表達式的方法。根據(jù)以下方式操作,功能內(nèi)部類中代碼可以實現(xiàn)覆蓋。如果項目中使用了線程池,也同樣可以依據(jù)此方法處理。

// mock transaction
when(mockTransaction.execute(any(TransactionCallback.class))).thenReturn(true);
// 匿名內(nèi)部類
when(mockTransaction.execute(Mockito.<TransactionCallback>any())).thenAnswer(new Answer<Object>() {
    public Object answer(InvocationOnMock invocation) {
        Object[] args = invocation.getArguments();
        TransactionCallback arg = (TransactionCallback) args[0];
        return arg.doInTransaction(new SimpleTransactionStatus());
    }
});
// lambda 方式
Answer<Object> answer = invocation -> {
    Object[] args = invocation.getArguments();
    TransactionCallback arg = (TransactionCallback) args[0];
    return arg.doInTransaction(new SimpleTransactionStatus());
};
when(mockTransaction.execute(any())).thenAnswer(answer);

3.6 mock http ResetTemplate

通常情況下 http 的調(diào)用是使用 httpUtils 工具類, 但是特殊的情況下使用 restTemplate 進行調(diào)用,如下所示,可以實現(xiàn)對 http 調(diào)用的 mock,這里只是一種 post 的調(diào)用方式,如果有其他的類型調(diào)用可以參考編寫 mock。

// mock http reset http
JSONObject body = new JSONObject();
body.put("code", "0000");
when(restTemplate.postForObject(anyString(), any(HttpEntity.class), eq(JSONObject.class))).thenReturn(body);

3.7 斷言

在編寫完成單元測試后,需要對結果進行斷言,通常情況下每個方法都需要有一個斷言,針對 void 方法,可以使用 verify 來進行處理,校驗方法中的某一個環(huán)節(jié)是否被處理過?,F(xiàn)在項目的集成與發(fā)版都實現(xiàn)了自動化,沒有斷言或者 verify 的方法會掃描出存在漏洞。斷言可以分為返回對象不為空或者返回值和預期值相同與否。verify 可以添加 times(1) 進行測試,校驗其方法調(diào)用的次數(shù)。

// 斷言返回對象不為空
Assert.assertNotNull(result);
// 斷言結果的期望值和結果值相同
Assert.assertEquals(result, expectedResult);
// 斷言方法中的某個環(huán)節(jié)被執(zhí)行過 調(diào)用一次
// times(n) 調(diào)用 n 次
// never() 沒有調(diào)用,相當于 調(diào)用 0 次 times(0)
// atMostOnce() 最多調(diào)用一次
// atLeastOnce() 最少調(diào)用一次
// atLeast() 最少一次
// atMost() 最多一次
verify(mockUserMapper, times(2)).selectByUserId(anyString());
verify(mockUserMapper, atLeast(1)).selectByUserId(anyString());

3.8 異常用例

以上講述的都是正常的單測,在實際的業(yè)務中還要模擬一些異常的場景,所以需要異常用例的編寫也是需要的,這樣進入到異常場景也可以提高單測的覆蓋率。

// 單測期望拋出一個異常信息
@Test(expected = RuntimeException.class)
public void testMockTest_TransactionTemplateThrowsTransactionException() throws Exception {
 ...
 // 異常操作      
when(mockTransaction.execute(any(TransactionCallback.class))).thenThrow(RuntimeException.class);
  ....
}

3.9 測試類的 setUp

通常情況下,在復雜的業(yè)務場景,需要對測試類設置屬性值,一般情況下屬性值都是從配置文件讀取,那怎么對其設置屬性值呢?這里用到了反射的知識,通過 hutool 工具類,可以對類的某個屬性賦值。同時也可以在這里做一些初始化的操作或者測試類單測前的準備工作。

@Before
public void setUp() {
    initMocks(this);
    // 使用反射的方式設置對象屬性的值
    ReflectionTestUtils.setField(mockBusinessUnderTest, "name", "test");
}

4 總結

在本文中,主要介紹了編寫單元測試的實踐,通過 squaretest 插件可以解決大部分的測試場景,如果有測試覆蓋不到的地方,無外乎以上介紹的幾種特殊的場景。掌握了以上的方式,可以很輕松的將單測覆蓋率提高到一個比較高的水平。

以上就是Springboot單元測試編寫實踐的詳細內(nèi)容,更多關于Springboot單元測試的資料請關注腳本之家其它相關文章!

相關文章

  • Maven添加Tomcat插件實現(xiàn)熱部署代碼實例

    Maven添加Tomcat插件實現(xiàn)熱部署代碼實例

    這篇文章主要介紹了Maven添加Tomcat插件實現(xiàn)熱部署代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-04-04
  • protobuf簡介及使用流程

    protobuf簡介及使用流程

    本文介紹了Protocol Buffers(protobuf)的數(shù)據(jù)結構序列化和反序列化框架,包括其特點、使用流程和快速上手,通過一個簡單的通訊錄示例,展示了如何創(chuàng)建.proto文件、添加注釋、編寫消息定義、編譯.proto文件以及進行序列化和反序列化操作,感興趣的朋友一起看看吧
    2025-02-02
  • SpringBoot如何使用mail實現(xiàn)登錄郵箱驗證

    SpringBoot如何使用mail實現(xiàn)登錄郵箱驗證

    在實際的開發(fā)當中,不少的場景中需要我們使用更加安全的認證方式,同時也為了防止一些用戶惡意注冊,我們可能會需要用戶使用一些可以證明個人身份的注冊方式,如短信驗證、郵箱驗證等,這篇文章主要介紹了SpringBoot如何使用mail實現(xiàn)登錄郵箱驗證,需要的朋友可以參考下
    2024-06-06
  • springboot接收json數(shù)據(jù)時,接收到空值問題

    springboot接收json數(shù)據(jù)時,接收到空值問題

    這篇文章主要介紹了springboot接收json數(shù)據(jù)時,接收到空值問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • Java實現(xiàn)簡單郵件發(fā)送

    Java實現(xiàn)簡單郵件發(fā)送

    這篇文章主要介紹了Java實現(xiàn)簡單郵件發(fā)送的相關資料,實例講解了java郵件發(fā)送實現(xiàn)方法,感興趣的小伙伴們可以參考一下
    2016-02-02
  • idea日志亂碼和tomcat日志亂碼問題的解決方法

    idea日志亂碼和tomcat日志亂碼問題的解決方法

    這篇文章主要介紹了idea日志亂碼和tomcat日志亂碼問題的解決方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-08-08
  • Java String 對象(你真的了解了嗎)

    Java String 對象(你真的了解了嗎)

    這篇文章主要介紹了Java String 對象(你真的了解了嗎),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-10-10
  • 阿里、華為、騰訊Java技術面試題精選

    阿里、華為、騰訊Java技術面試題精選

    這篇文章主要為大家分享了阿里、華為、騰訊Java技術面試題精選,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • SpringCloud讓微服務實現(xiàn)指定程序調(diào)用

    SpringCloud讓微服務實現(xiàn)指定程序調(diào)用

    這篇文章主要介紹了SpringCloud讓微服務實現(xiàn)指定程序調(diào)用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-06-06
  • springboot項目打docker鏡像實例(入門級)

    springboot項目打docker鏡像實例(入門級)

    最近做個項目,我們想把自己的程序打包成鏡像,并運行在docker容器中,本文主要介紹了springboot項目打docker鏡像實例,具有一定的參考價值,感興趣的可以了解一下
    2024-06-06

最新評論