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

Springboot單元測(cè)試編寫(xiě)實(shí)踐

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

1 前言

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

2 生成單元測(cè)試

通常情況下,單元測(cè)試都是使用 junit 編寫(xiě)的,但是這種方式會(huì)真實(shí)的調(diào)用數(shù)據(jù),如何優(yōu)雅的實(shí)現(xiàn)單元測(cè)試是一個(gè)問(wèn)題。這里使用的是 powermock 來(lái)實(shí)現(xiàn)測(cè)試用例的編寫(xiě)。引入 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>

在編寫(xiě)單元測(cè)試之前,需要在 idea 中安裝一個(gè) squaretest 插件,在需要編寫(xiě)單元測(cè)試的類中,通過(guò)右鍵 generate -> generate Test 可以打開(kāi)一個(gè)面板,可以選擇一個(gè)模板,就可以在test中生成具體的單元測(cè)試類。

可以這樣說(shuō),有了這個(gè)便捷的插件助力,對(duì)于簡(jiǎn)單的場(chǎng)景,就可以完全覆蓋其業(yè)務(wù)功能。

3 單測(cè)重點(diǎn)

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

3.1 mock static 方法

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

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

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

3.2 mock 分布式鎖

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

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

3.3 mock spring 中的 bean

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

// mock 業(yè)務(wù)查詢和操作
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 操作

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

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

3.5 mock 事務(wù) transactionTemplate

事務(wù)的操作是在特殊業(yè)務(wù)場(chǎng)景下才會(huì)用到,這里的只是借此場(chǎng)景來(lái)說(shuō)明如何對(duì)匿名類中的方法進(jìn)行單測(cè)。默認(rèn)情況下生成的測(cè)試代碼是不具備這種能力的,需要使用 thenAnswer 來(lái)處理。以下列舉了兩種方式,一種是匿名類的方式,一種是 lambda 表達(dá)式的方法。根據(jù)以下方式操作,功能內(nèi)部類中代碼可以實(shí)現(xiàn)覆蓋。如果項(xiàng)目中使用了線程池,也同樣可以依據(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 進(jìn)行調(diào)用,如下所示,可以實(shí)現(xiàn)對(duì) http 調(diào)用的 mock,這里只是一種 post 的調(diào)用方式,如果有其他的類型調(diào)用可以參考編寫(xiě) 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 斷言

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

// 斷言返回對(duì)象不為空
Assert.assertNotNull(result);
// 斷言結(jié)果的期望值和結(jié)果值相同
Assert.assertEquals(result, expectedResult);
// 斷言方法中的某個(gè)環(huán)節(jié)被執(zhí)行過(guò) 調(diào)用一次
// times(n) 調(diào)用 n 次
// never() 沒(méi)有調(diào)用,相當(dāng)于 調(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 異常用例

以上講述的都是正常的單測(cè),在實(shí)際的業(yè)務(wù)中還要模擬一些異常的場(chǎng)景,所以需要異常用例的編寫(xiě)也是需要的,這樣進(jìn)入到異常場(chǎng)景也可以提高單測(cè)的覆蓋率。

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

3.9 測(cè)試類的 setUp

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

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

4 總結(jié)

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

以上就是Springboot單元測(cè)試編寫(xiě)實(shí)踐的詳細(xì)內(nèi)容,更多關(guān)于Springboot單元測(cè)試的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

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

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

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

    protobuf簡(jiǎn)介及使用流程

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

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

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

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

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

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

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

    idea日志亂碼和tomcat日志亂碼問(wèn)題的解決方法

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

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

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

    阿里、華為、騰訊Java技術(shù)面試題精選

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

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

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

    springboot項(xiàng)目打docker鏡像實(shí)例(入門級(jí))

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

最新評(píng)論