chinese直男口爆体育生外卖, 99久久er热在这里只有精品99, 又色又爽又黄18禁美女裸身无遮挡, gogogo高清免费观看日本电视,私密按摩师高清版在线,人妻视频毛茸茸,91论坛 兴趣闲谈,欧美 亚洲 精品 8区,国产精品久久久久精品免费

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

一種極簡單的SpringBoot單元測試方法

京東云 ? 來源:jf_75140285 ? 作者:jf_75140285 ? 2025-03-11 15:39 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

前言

本文主要提供了一種單元測試方法,力求0基礎(chǔ)人員可以從本文中受到啟發(fā),可以搭建一套好用的單元測試環(huán)境,并能切實(shí)的提高交付代碼的質(zhì)量。極簡體現(xiàn)在除了POM依賴和單元測試類之外,其他什么都不需要引入,只需要一個本地能啟動的springboot項(xiàng)目。

目錄

1.POM依賴

2.單元測試類示例及注解釋義

3.單元測試經(jīng)驗(yàn)總結(jié)

一、POM依賴

Springboot版本: 2.6.6


  org.springframework.boot
  spring-boot-starter-test
  test


  org.mockito
  mockito-core
  3.12.4



二、單元測試類示例

主要有兩種

第一種,偏集成測試

需要啟動項(xiàng)目,需要連接數(shù)據(jù)庫、RPC注冊中心

主要注解:@SpringBootTest + @RunWith(SpringRunner.class) + @Transactional + @Resource + @SpyBean + @Test

?@SpringBootTest + @RunWith(SpringRunner.class) 啟動了一套springboot的測試環(huán)境;

?@Transactional 對于一些修改數(shù)據(jù)庫的操作,會執(zhí)行回滾,能測試執(zhí)行sql,但是又不會真正的修改測試庫的數(shù)據(jù);

?@Resource 主要引入被測試的類

?@SpyBean springboot環(huán)境下mock依賴的bean,可以搭配Mockito.doAnswer(…).when(xxServiceImpl).xxMethod(any())mock特定方法的返回值;

?@Test 標(biāo)識一個測試方法

TIP:對于打樁有這幾個注解@Mock @Spy @MockBean @SpyBean,每一個都有其對應(yīng)的搭配,簡單說@Mock和@Spy要搭配@InjectMocks去使用,@MockBean和@SpyBean搭配@SpringBootTest + @RunWith(SpringRunner.class)使用,@InjectMocks不用啟動應(yīng)用,它啟動了一個完全隔離的測試環(huán)境,無法使用spring提供的所有bean,所有的依賴都需要被mock

上代碼:

/**
 * @author jiangbo8
 * @since 2024/4/24 9:52
 */
@Transactional
@SpringBootTest
@RunWith(SpringRunner.class)
public class SalesAmountPlanControllerAppTest {
    @Resource
    private SalesAmountPlanController salesAmountPlanController;
    @SpyBean
    private ISaleAmountHourHistoryService saleAmountHourHistoryServiceImpl;
    @SpyBean
    private ISaleAmountHourForecastService saleAmountHourForecastServiceImpl;
    @SpyBean
    private ISaleAmountHourPlanService saleAmountHourPlanServiceImpl;

    @Test
    public void testGraph1()  {
        // 不寫mock就走實(shí)際調(diào)用

        SalesAmountDTO dto = new SalesAmountDTO();
        dto.setDeptId1List(Lists.newArrayList(35));
        dto.setDeptId2List(Lists.newArrayList(235));
        dto.setDeptId3List(Lists.newArrayList(100));
        dto.setYoyType(YoyTypeEnum.SOLAR.getCode());
        dto.setShowWeek(true);
        dto.setStartYm("2024-01");
        dto.setEndYm("2024-10");
        dto.setTimeDim(GraphTimeDimensionEnum.MONTH.getCode());
        dto.setDataType(SalesAmountDataTypeEnum.AMOUNT.getCode());
        Result result = salesAmountPlanController.graph(dto);
        System.out.println(JSON.toJSONString(result));
        Assert.assertNotNull(result);
    }

    @Test
    public void testGraph11()  {
        // mock就走mock
        Mockito.doAnswer(this::mockSaleAmountHourHistoryListQuery).when(saleAmountHourHistoryServiceImpl).listBySaleAmountQueryBo(any());
        Mockito.doAnswer(this::mockSaleAmountHourPlansListQuery).when(saleAmountHourPlanServiceImpl).listBySaleAmountQueryBo(any());
        Mockito.doAnswer(this::mockSaleAmountHourForecastListQuery).when(saleAmountHourForecastServiceImpl).listBySaleAmountQueryBo(any());

        SalesAmountDTO dto = new SalesAmountDTO();
        dto.setDeptId1List(Lists.newArrayList(111));
        dto.setDeptId2List(Lists.newArrayList(222));
        dto.setDeptId3List(Lists.newArrayList(333));
        dto.setYoyType(YoyTypeEnum.SOLAR.getCode());
        dto.setShowWeek(true);
        dto.setStartYm("2024-01");
        dto.setEndYm("2024-10");
        dto.setTimeDim(GraphTimeDimensionEnum.MONTH.getCode());
        dto.setDataType(SalesAmountDataTypeEnum.AMOUNT.getCode());
        Result result = salesAmountPlanController.graph(dto);
        System.out.println(JSON.toJSONString(result));
        Assert.assertNotNull(result);
    }
    
	private List mockSaleAmountHourHistoryListQuery(org.mockito.invocation.InvocationOnMock s) {
        SaleAmountQueryBo queryBo = s.getArgument(0);
        if (queryBo.getGroupBy().contains("ymd")) {
            List historyList = Lists.newArrayList();
            List ymdList = DateUtil.rangeWithDay(DateUtil.parseFirstDayLocalDate(queryBo.getStartYm()), DateUtil.parseLastDayLocalDate(queryBo.getStartYm()));
            for (String ymd : ymdList) {
                SaleAmountHourHistory history = new SaleAmountHourHistory();
                history.setYear(Integer.parseInt(queryBo.getStartYm().split("-")[0]));
                history.setMonth(Integer.parseInt(queryBo.getStartYm().split("-")[1]));
                history.setYm(queryBo.getStartYm());
                history.setYmd(DateUtil.parseLocalDateByYmd(ymd));

                history.setAmount(new BigDecimal("1000"));
                history.setAmountSp(new BigDecimal("2000"));
                history.setAmountLunarSp(new BigDecimal("3000"));

                history.setSales(new BigDecimal("100"));
                history.setSalesSp(new BigDecimal("200"));
                history.setSalesLunarSp(new BigDecimal("300"));

                history.setCostPrice(new BigDecimal("100"));
                history.setCostPriceSp(new BigDecimal("100"));
                history.setCostPriceLunarSp(new BigDecimal("100"));
                historyList.add(history);
            }

            return historyList;
        }

        List ymList = DateUtil.rangeWithMonth(DateUtil.parseFirstDayLocalDate(queryBo.getStartYm()), DateUtil.parseLastDayLocalDate(queryBo.getEndYm()));
        List historyList = Lists.newArrayList();
        for (String ym : ymList) {
            SaleAmountHourHistory history = new SaleAmountHourHistory();
            history.setYear(Integer.parseInt(ym.split("-")[0]));
            history.setMonth(Integer.parseInt(ym.split("-")[1]));
            history.setYm(ym);

            history.setAmount(new BigDecimal("10000"));
            history.setAmountSp(new BigDecimal("20000"));
            history.setAmountLunarSp(new BigDecimal("30000"));

            history.setSales(new BigDecimal("1000"));
            history.setSalesSp(new BigDecimal("2000"));
            history.setSalesLunarSp(new BigDecimal("3000"));

            history.setCostPrice(new BigDecimal("100"));
            history.setCostPriceSp(new BigDecimal("100"));
            history.setCostPriceLunarSp(new BigDecimal("100"));
            historyList.add(history);
        }

        return historyList;
    } 
}

第二種,單元測試

不需要啟動項(xiàng)目,也不會連接數(shù)據(jù)庫、RPC注冊中心等,但是相應(yīng)的所有數(shù)據(jù)都需要打樁mock

這種方法可以使用testMe快速生成單元測試類的框架,具體方法見: 基于testMe快速生成單元測試類(框架)

主要注解:@InjectMocks + @Mock + @Test

?@InjectMocks標(biāo)識了一個需要被測試的類,這個類中依賴的bean都需要被@Mock,并mock返回值,不然就會空指針

?@Mock mock依賴,具體mock數(shù)據(jù)還要搭配when(xxService.xxMethod(any())).thenReturn(new Object()); mock返回值

?@Test 標(biāo)識一個測試方法

上代碼:

/**
 * Created by jiangbo8 on 2022/10/17 15:02
 */
public class CheckAndFillProcessorTest {
    @Mock
    Logger log;
    @Mock
    OrderRelService orderRelService;
    @Mock
    VenderServiceSdk venderServiceSdk;
    @Mock
    AfsServiceSdk afsServiceSdk;
    @Mock
    PriceServiceSdk priceServiceSdk;
    @Mock
    ProductInfoSdk productInfoSdk;
    @Mock
    OrderMidServiceSdk orderMidServiceSdk;
    @Mock
    OrderQueueService orderQueueService;
    @Mock
    SendpayMarkService sendpayMarkService;
    @Mock
    TradeOrderService tradeOrderService;

    @InjectMocks
    CheckAndFillProcessor checkAndFillProcessor;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testProcess2() throws Exception {

        OrderRel orderRel = new OrderRel();
        //orderRel.setJdOrderId(2222222L);
        orderRel.setSopOrderId(1111111L);
        orderRel.setVenderId("123");

        when(orderRelService.queryOrderBySopOrderId(anyLong())).thenReturn(orderRel);

        OrderDetailRel orderDetailRel = new OrderDetailRel();
        orderDetailRel.setJdSkuId(1L);
        when(orderRelService.queryDetailList(any())).thenReturn(Collections.singletonList(orderDetailRel));

        Vender vender = new Vender();
        vender.setVenderId("123");
        vender.setOrgId(1);
        when(venderServiceSdk.queryVenderByVenderId(anyString())).thenReturn(vender);
        when(afsServiceSdk.queryAfsTypeByJdSkuAndVender(anyLong(), anyString())).thenReturn(0);
        when(priceServiceSdk.getJdToVenderPriceByPriorityAndSaleTime(anyString(), anyString(), any())).thenReturn(new BigDecimal("1"));
        when(productInfoSdk.getProductInfo(any())).thenReturn(new HashMap>() {{
            put(1L, new HashMap() {{
                put("String", "String");
            }});
        }});

        when(orderQueueService.updateQueueBySopOrderId(any())).thenReturn(true);

        Order sopOrder = new Order();
        sopOrder.setYn(1);
        when(orderMidServiceSdk.getOrderByIdFromMiddleWare(anyLong())).thenReturn(sopOrder);

        when(sendpayMarkService.isFreshOrder(anyLong(), anyString())).thenReturn(true);

        doNothing().when(tradeOrderService).fillOrderProduceTypeInfo(any(), anyInt(), any());
        doNothing().when(tradeOrderService).fillOrderFlowFlagInfo(any(), any(), anyInt(), any());

        Field field = ResourceContainer.class.getDeclaredField("allInPlateConfig");
        field.setAccessible(true);
        field.set("allInPlateConfig", new AllInPlateConfig());

        OrderQueue orderQueue = new OrderQueue();
        orderQueue.setSopOrderId(1111111L);
        DispatchResult result = checkAndFillProcessor.process(orderQueue);
        Assert.assertNotNull(result);
    }
}

三、單元測試經(jīng)驗(yàn)總結(jié)

在工作中總結(jié)了一些單元測試的使用場景:

1.重構(gòu),如果我們拿到了一個代碼,我們要去重構(gòu)這個代碼,如果這個代碼本身的單元測試比較完善,那么我們重構(gòu)完之后可以執(zhí)行一下現(xiàn)有的單元測試,以保證重構(gòu)前后代碼在各個場景的邏輯保證最終一致,但是如果單元測試不完善甚至沒有,那我建議大家可以基于AI去生成這個代碼的單元測試,然后進(jìn)行重構(gòu),再用生成的單元測試去把控質(zhì)量,這里推薦Diffblue去生成,有興趣的可以去了解一下。

2.新功能,新功能建議使用上面推薦的兩種方法去做單測,第一種方法因?yàn)槠蓽y試,單元測試代碼編寫的壓力比較小,可以以黑盒測試的視角去覆蓋測試case就可以了,但是如果某場景極為復(fù)雜,想要單獨(dú)對某個復(fù)雜計(jì)算代碼塊進(jìn)行專門的測試,那么可以使用第二種方法,第二種方法是很單純的單元測試,聚焦專門代碼塊,但是如果普遍使用的話,單元測試代碼編寫量會很大,不建議單純使用某一種,可以具體情況具體分析。

建議大家做單元測試不要單純的追求行覆蓋率,還是要本著提高質(zhì)量的心態(tài)去做單元測試。

審核編輯 黃宇

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 測試
    +關(guān)注

    關(guān)注

    8

    文章

    5898

    瀏覽量

    130252
  • 單元測試
    +關(guān)注

    關(guān)注

    0

    文章

    50

    瀏覽量

    3427
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關(guān)推薦
    熱點(diǎn)推薦

    電源與時鐘的單元測試方案解析

    單元測試的問題全部要提問題單跟蹤解決,測出問題在記錄在跟蹤表的同時就馬上提問題單(單元測試的問題單可以走短流程),不要積累到最后起提。
    發(fā)表于 03-03 14:45 ?1479次閱讀

    MCU進(jìn)行單元測試方法

    背景MCU軟件不同于常規(guī)的PC機(jī)或基于SOC的嵌入式軟件,其般情況下,與底層硬件耦合度高,資源有限,如何進(jìn)行單元測試的問題困擾我很久。解決方案根據(jù)目前已知如下3類型的方案:在目標(biāo)板上運(yùn)行此方案下,在程序代碼中加入
    發(fā)表于 11-01 06:58

    單元測試/集成測試自動化工具--WinAMS

    的對安全性要求極高的領(lǐng)域,單元測試已經(jīng)成為不可缺少的部分。使用目標(biāo)機(jī)代碼進(jìn)行單元測試也是為了符合汽車行業(yè)中ISO26262功能安全認(rèn)證標(biāo)準(zhǔn)。產(chǎn)品特長全面支持嵌入式微機(jī)!驗(yàn)證嵌入式C/C++軟件 實(shí)施以模塊
    發(fā)表于 06-17 18:26

    如何提高嵌入式軟件單元測試效率

    每個代碼片段是否按預(yù)期工作。 如果運(yùn)行回歸測試套件太耗時: ·工程資源在等待測試完成時未被使用·代碼質(zhì)量因代碼測試頻率降低而降低·上市時間增加本指南介紹了一種使用虛擬平臺的
    發(fā)表于 08-28 06:31

    系統(tǒng)測試、單元測試、集成測試、驗(yàn)收測試、回歸測試

    系統(tǒng)測試單元測試、集成測試、驗(yàn)收測試、回歸測試 單元測試
    發(fā)表于 10-22 12:38 ?1993次閱讀

    單元測試常用的方法

    單元測試,是指對軟件中的最小可測試單元進(jìn)行檢查和驗(yàn)證。對于單元測試單元的含義,般來說,要根據(jù)
    發(fā)表于 12-21 10:17 ?3.8w次閱讀
    <b class='flag-5'>單元測試</b>常用的<b class='flag-5'>方法</b>

    什么是單元測試_單元測試的目的是什么

    工廠在組裝臺電視機(jī)之前,會對每個元件都進(jìn)行測試,這,就是單元測試單元測試是開發(fā)者編寫的小段代碼,用于檢驗(yàn)被測代碼的
    發(fā)表于 12-21 13:44 ?3.4w次閱讀

    java單元測試的好處

    單元測試是編寫測試代碼,應(yīng)該準(zhǔn)確、快速地保證程序基本模塊的正確性。好的單元測試的標(biāo)準(zhǔn),JUnit是Java單元測試框架,已經(jīng)在Eclipse中默認(rèn)安裝。許多開發(fā)者都有個習(xí)慣,常常不樂意
    發(fā)表于 12-21 14:24 ?4171次閱讀

    java單元測試怎么寫

    Java是門面向?qū)ο缶幊陶Z言,不僅吸收了C++語言的各種優(yōu)點(diǎn),還摒棄了C++里難以理解的多繼承、指針等概念,因此Java語言具有功能強(qiáng)大和簡單易用兩個特征。單元測試,是指對軟件中的最小可測試
    發(fā)表于 12-21 14:54 ?8684次閱讀
    java<b class='flag-5'>單元測試</b>怎么寫

    什么是單元測試,為什么要做單元測試

    。 什么是單元測試? 單元測試(unit testing),是指對軟件中的最小可測試單元進(jìn)行檢查和驗(yàn)證。通常而言,
    的頭像 發(fā)表于 04-28 17:21 ?1.1w次閱讀

    MCU如何進(jìn)行單元測試

    背景MCU軟件不同于常規(guī)的PC機(jī)或基于SOC的嵌入式軟件,其般情況下,與底層硬件耦合度高,資源有限,如何進(jìn)行單元測試的問題困擾我很久。解決方案根據(jù)目前已知如下3類型的方案:在目標(biāo)板上運(yùn)行此方案下,在程序代碼中加入
    發(fā)表于 10-26 10:06 ?35次下載
    MCU如何進(jìn)行<b class='flag-5'>單元測試</b>

    RT-Thread上的單元測試:什么是單元測試?單元測試的作用是什么?

    RT-Thread上的單元測試:什么是單元測試單元測試的作用是什么? ? ? ? ? ? 審核編輯:彭靜
    的頭像 發(fā)表于 05-27 16:06 ?2146次閱讀
    RT-Thread上的<b class='flag-5'>單元測試</b>:什么是<b class='flag-5'>單元測試</b>?<b class='flag-5'>單元測試</b>的作用是什么?

    軟件單元測試真的有必要嗎?(上)

    本文著重探討單元測試的重要性及其正面臨的困境,并介紹功能安全標(biāo)準(zhǔn)中羅列的單元測試方法。
    的頭像 發(fā)表于 11-03 14:58 ?1404次閱讀
    軟件<b class='flag-5'>單元測試</b>真的有必要嗎?(上)

    一種通用的汽車車身電子單元測試工裝的研究設(shè)計(jì)

    電子發(fā)燒友網(wǎng)站提供《一種通用的汽車車身電子單元測試工裝的研究設(shè)計(jì).pdf》資料免費(fèi)下載
    發(fā)表于 11-07 10:07 ?1次下載
    <b class='flag-5'>一種</b>通用的汽車車身電子<b class='flag-5'>單元測試</b>工裝的研究設(shè)計(jì)

    邊聊安全 | 軟件單元測試的設(shè)計(jì)方法

    的設(shè)計(jì)是確保代碼正確性和可靠性的關(guān)鍵步驟。在軟件單元測試中,等價類測試一種很重要的測試設(shè)計(jì)方法,它通過將輸入數(shù)據(jù)劃分為若干個等價類,并從每
    的頭像 發(fā)表于 09-05 16:18 ?1559次閱讀
    邊聊安全 | 軟件<b class='flag-5'>單元測試</b>的設(shè)計(jì)<b class='flag-5'>方法</b>