女人自慰AV免费观看内涵网,日韩国产剧情在线观看网址,神马电影网特片网,最新一级电影欧美,在线观看亚洲欧美日韩,黄色视频在线播放免费观看,ABO涨奶期羡澄,第一导航fulione,美女主播操b

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

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

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

一種極簡(jiǎn)單的SpringBoot單元測(cè)試方法

京東云 ? 來源:jf_75140285 ? 作者:jf_75140285 ? 2025-03-11 15:39 ? 次閱讀

前言

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

目錄

1.POM依賴

2.單元測(cè)試類示例及注解釋義

3.單元測(cè)試經(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



二、單元測(cè)試類示例

主要有兩種

第一種,偏集成測(cè)試

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

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

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

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

?@Resource 主要引入被測(cè)試的類

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

?@Test 標(biāo)識(shí)一個(gè)測(cè)試方法

TIP:對(duì)于打樁有這幾個(gè)注解@Mock @Spy @MockBean @SpyBean,每一個(gè)都有其對(duì)應(yīng)的搭配,簡(jiǎn)單說@Mock和@Spy要搭配@InjectMocks去使用,@MockBean和@SpyBean搭配@SpringBootTest + @RunWith(SpringRunner.class)使用,@InjectMocks不用啟動(dòng)應(yīng)用,它啟動(dòng)了一個(gè)完全隔離的測(cè)試環(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;
    } 
}

第二種,單元測(cè)試

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

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

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

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

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

?@Test 標(biāo)識(shí)一個(gè)測(cè)試方法

上代碼:

/**
 * 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);
    }
}

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

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

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

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

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

審核編輯 黃宇

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

    關(guān)注

    8

    文章

    5629

    瀏覽量

    128308
  • 單元測(cè)試
    +關(guān)注

    關(guān)注

    0

    文章

    49

    瀏覽量

    3278
收藏 人收藏

    評(píng)論

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

    電源與時(shí)鐘的單元測(cè)試方案解析

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

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

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

    單元測(cè)試/集成測(cè)試自動(dòng)化工具--WinAMS

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

    如何提高嵌入式軟件單元測(cè)試效率

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

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

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

    單元測(cè)試常用的方法

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

    什么是單元測(cè)試_單元測(cè)試的目的是什么

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

    java單元測(cè)試的好處

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

    java單元測(cè)試怎么寫

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

    什么是單元測(cè)試,為什么要做單元測(cè)試

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

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

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

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

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

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

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

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

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

    嵌入軟件單元測(cè)試工具的作用

    嵌入軟件單元測(cè)試工具是現(xiàn)代軟件開發(fā)過程中不可或缺的環(huán)。它的作用在于幫助開發(fā)人員對(duì)軟件中的各個(gè)單元進(jìn)行測(cè)試,以確保其功能的正確性和穩(wěn)定性。單元測(cè)試
    的頭像 發(fā)表于 04-23 15:31 ?637次閱讀
    嵌入軟件<b class='flag-5'>單元測(cè)試</b>工具的作用