junit+mockito-PowerMock完成单测

Mockito简介

什么是Mockito

Mockito是一个开源的Mock框架,旨在为Java单元测试提供简单、可读的Mock对象。它可以模拟类的行为,使测试代码能够在不依赖真实对象的情况下运行。

为什么使用Mockito

  • 隔离外部依赖:可以模拟外部系统(如数据库、网络服务等),使测试更加独立。
  • 提高测试覆盖率:即使没有实现具体逻辑,也可以编写测试,确保接口和交互逻辑正确。
  • 便于测试边界条件:可以轻松模拟异常情况和边界条件,确保代码在各种情况下的稳定性。

中文文档

https://gitcode.com/bboyfeiyu/mockito-doc-zh/overview?utm_source=artical_gitcode

https://github.com/kuaile-zc/mockito-doc-zh/tree/master

单测mock

mock结果

复制代码
   PageQueryAwardPunishmentReqDTO queryDTO2 = new PageQueryAwardPunishmentReqDTO();
        queryDTO2.setStartTime(command.getStartCreatedTime());
        queryDTO2.setEndTime(command.getEndCreatedTime());
        queryDTO2.setAwardPunishmentNoList(command.getAwardPunishmentNoList());
        queryDTO2.setAuditStatusList(Collections.singletonList(AwardPunishmentAuditStatusEnum.WAIT_AUDIT));
        queryDTO2.setStatusList(Collections.singletonList(AwardPunishmentStatusEnum.NORMAL));
        queryDTO2.setCurrentPage(1L);
        queryDTO2.setGtId(awardPunishmentAggregate1.getId());
        queryDTO2.setPageSize(awardPunishmentApolloConfig.getCancelMaxBatchSize().longValue());
        // 模拟分页查询结果
        IPage<AwardPunishmentAggregate> pageResult2 = new Page<>();
        pageResult2.setPages(2);
        pageResult2.setTotal(2);
        pageResult2.setSize(awardPunishmentApolloConfig.getCancelMaxBatchSize());
        pageResult2.setRecords(Collections.singletonList(initPushAwardPunishmentAggregate("AP1820569200913880330")));
        when(awardPunishmentRepository.pageAwardPunishment(queryDTO2)).thenReturn(pageResult1).thenReturn(pageResult2);//写2个是内部有for循环模拟调用2次
复制代码

 

监测变量

复制代码
 // Run the test
        final List<String> result = coldChainShipmentOrderDomainServiceImplUnderTest.createShipmentOrder(command,
            OperateContextUtil.getSystemOperator());
        //监测此变量
        ArgumentCaptor<List<ColdChainShipmentOrderAggregate>> captor = ArgumentCaptor.forClass(
            List.class);
        //内部调用此mock方法时监测
        verify(mockColdChainShipmentOrderRepository).doSave(captor.capture());
        // 获取捕获的参数值
        List<ColdChainShipmentOrderAggregate> capturedValue = captor.getValue();

        //mock的数据未经过习惯没有domainEvents
        assertThat(CollectionUtils.isNotEmpty(capturedValue) && capturedValue.size() == 1
            && capturedValue.get(0).getDomainEvents().get(0) instanceof CreateShipmentOrderDomainEvent).isTrue();
复制代码

 

 状态清除

 Mockito.reset(mockRedissonLockUtil);
        Mockito.reset(mockColdChainAlarmOrderRepository);
        Mockito.reset(mockNotifyApplicationService);
        Mockito.reset(mockNotifyApplicationService);

 mock void方法抛出异常

        doThrow(new YxtRuntimeException(ResponseCodeType.BIZ_EXCEPTION)).when(mockAuthApplicationService).checkAuthByStoreCode(command.getShipperOrgCode(),
            any(OperateContext.class));

mock void什么都不做

// 模拟校验通过(无异常抛出)
        doNothing().when(awardPunishmentRepresentationService).checkAuditAwardPunishment(command, operateContext);

 

 

捕获

变量捕获

复制代码
        //------------------------------------------------------根据发货单号进行发货----------------------------------
        // Run the test
        final List<String> result = coldChainShipmentOrderDomainServiceImplUnderTest.batchShipOrder(batchShipCommand,
            operateContext);

        ArgumentCaptor<List<ColdChainShipmentOrderAggregate>> captor = ArgumentCaptor.forClass(
            List.class);
        verify(mockColdChainShipmentOrderRepository).doSave(captor.capture());
        // 获取捕获的参数值
        List<ColdChainShipmentOrderAggregate> capturedValue = captor.getValue();
        assertThat(capturedValue.get(0).getShipmentInfo() != null).isTrue();
复制代码

 多次调用捕获

复制代码
        // 验证
        verify(awardPunishmentRepository, times(2)).pageAwardPunishment(any());
        ArgumentCaptor<List<AwardPunishmentAggregate>> captor = ArgumentCaptor.forClass(
            List.class);
        verify(awardPunishmentRepository, times(2)).doSave(captor.capture());
        List<List<AwardPunishmentAggregate>> allValues = captor.getAllValues();
        for (List<AwardPunishmentAggregate> allValue : allValues) {
            assertThat(CollectionUtils.isNotEmpty(allValue)).isTrue();
            for (AwardPunishmentAggregate awardPunishmentAggregate : allValue) {
                assertThat(
                    AwardPunishmentStatusEnum.CANCEL == awardPunishmentAggregate.getAwardPunishmentStatus()).isTrue();
            }
        }
复制代码

 

 

常用断言

断言方法被调用

  verify(mockRedissonLockUtil).unlock(lockKey);

 

断言方法未被调用

verify(mockNotifyApplicationService, never()).sendPersonMessage(any(SendPersonMessageDTO.class),
    any(OperateContext.class));

 断言抛出异常

        assertThatThrownBy(
            () -> coldChainAlarmOrderDomainServiceImplUnderTest.createAlarmOrder(command))
            .isInstanceOf(YxtRuntimeException.class).hasMessage("系统繁忙,请稍后重试!");

模拟事务

   // 发送领域事件
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
            @Override
            public void afterCommit() {
              
            }
        });

 

    @Before
    public void startTransaction() {
        if (!TransactionSynchronizationManager.isActualTransactionActive()) {
            TransactionSynchronizationManager.setActualTransactionActive(true);
            TransactionSynchronizationManager.initSynchronization();
        }
    }

 

PowerMock

mock

mock私有方法

复制代码
@RunWith(PowerMockRunner.class)
@PrepareForTest({PatrolRectifyDomainServiceImpl.class})
public class PatrolRectifyDomainServiceImplTest {

    @Mock
    private YxtTaskAdaptor yxtTaskAdaptor;
    @Mock
    private PatrolRectifyRepository patrolRectifyRepository;

    @InjectMocks
    private PatrolRectifyDomainServiceImpl patrolRectifyDomainService;

    //region testCloseTask

    /**
     * 测试关闭任务时,任务已过期的情况。
     */
    @Test
    public void testCloseTask_ExpiredTask() throws Exception {
        // 初始化测试数据
        String patrolRectifyNo = "NO123";
        OperateContext operateContext = OperateContextUtil.getSystemOperator();
        PatrolRectifyAggregate aggregate = new PatrolRectifyAggregate();
        aggregate.setRectifyEndTime(DateHelper.getDayNow(DateHelper.now(), -1)); // 设置为昨天,表示已过期
        aggregate.setRectifyStatus(PatrolRectifyStatusEnum.CANCELLED);

        // 模拟 getAndCheckPatrolRectifyAggregate 方法返回已过期的任务
        // Mock 模拟私有方法
        PatrolRectifyDomainServiceImpl spyService = PowerMockito.spy(
            patrolRectifyDomainService);

        PowerMockito.doReturn(aggregate)
            .when(spyService, "getAndCheckPatrolRectifyAggregate", any());
        // 执行测试
        spyService.closeTask(patrolRectifyNo, operateContext);

        // 验证
        verify(yxtTaskAdaptor, never()).endSingleTask(any(), any());
    }
}
复制代码

 

 

初始化私有变量

        Whitebox.setInternalState(coldChainAlarmOrderRepresentationServiceImplUnderTest, "chainBusinessOrgCode",
            chainBusinessOrgCodeValue);

 私有方法单测

复制代码
  @InjectMocks
    private ReceiveApplicationServiceImpl receiveApplicationServiceImplUnderTest;
    @Test
    public void test_checkReceiveTime() {

        ReceiveApplicationServiceImpl spyService = PowerMockito.spy(
            receiveApplicationServiceImplUnderTest);
        ReceiveCommand command = ReceiveCommand.builder().receiverTime(DateHelper.getDayNow(DateHelper.now(), 1))
            .build();
        //发货信息为空场景
        ColdChainShipmentInfoEntity coldChainShipmentInfoEntity = null;
        assertThatThrownBy(
            () -> Whitebox.invokeMethod(spyService, "checkReceiveTime", command, coldChainShipmentInfoEntity))
            .isInstanceOf(YxtRuntimeException.class).hasMessage("发货信息不能为空");

        //收货时间早于发货时间场景
        ColdChainShipmentInfoEntity coldChainShipmentInfoEntityA = new ColdChainShipmentInfoEntity();
        coldChainShipmentInfoEntityA.setDepartureTime(DateHelper.getDayNow(DateHelper.now(), 2));
        assertThatThrownBy(
            () -> Whitebox.invokeMethod(spyService, "checkReceiveTime", command, coldChainShipmentInfoEntityA))
            .isInstanceOf(YxtRuntimeException.class).hasMessage("收货时间早于发货时间,不能收货");

        //收货时间大于当前时间场景
        ReceiveCommand commandB = ReceiveCommand.builder().receiverTime(DateHelper.getDayNow(DateHelper.now(), 1))
            .build();
        ColdChainShipmentInfoEntity coldChainShipmentInfoEntityB = new ColdChainShipmentInfoEntity();
        coldChainShipmentInfoEntityB.setDepartureTime(DateHelper.getDayNow(DateHelper.now(), -2));
        assertThatThrownBy(
            () -> Whitebox.invokeMethod(spyService, "checkReceiveTime", commandB, coldChainShipmentInfoEntityB))
            .isInstanceOf(YxtRuntimeException.class).hasMessage("收货时间晚于当前时间,不能收货");
    }
复制代码

mock静态方法

复制代码
@RunWith(PowerMockRunner.class)
@PrepareForTest(HttpRequest.class)
@PowerMockIgnore({"javax.management.*"})
public class ErpAdaptorImplMockTest {

    @InjectMocks
    private ErpAdaptorImpl erpAdaptor;

    @Mock
    private ErpApolloConfig erpApolloConfig;

    @Mock
    private StringRedisTemplate stringRedisTemplate;

    private ClaimBankAccountAntiReqDTO antiReqDTO;

    /**
     * 测试:接口调用失败 预期:返回接口调用异常的结果
     */
    @Test
    public void testClaimBankAccount_RequestFailed() throws Exception {
        // 模拟接口配置
        InterfaceInfo interfaceInfo = new InterfaceInfo();
        interfaceInfo.setType(ErpInterfaceEnum.CLAIM_BANK_ACCOUNT.name());
        interfaceInfo.setInterfaceUrl("https://erp.example.com/claim");
        interfaceInfo.setOpenCall(true);
        given(erpApolloConfig.getInterfaceInfoByType(any())).willReturn(Optional.of(interfaceInfo));

        // 模拟 ERP 接口调用失败
        HttpRequest httpClient = mock(HttpRequest.class);

        PowerMockito.mockStatic(HttpRequest.class);
        PowerMockito.when(HttpRequest.post(any())).thenReturn(httpClient);
        given(httpClient.addHeaders(any())).willReturn(httpClient);
        given(httpClient.body(anyString())).willReturn(httpClient);
        given(httpClient.timeout(anyInt())).willReturn(httpClient);

        HttpResponse httpResponse = mock(HttpResponse.class);
        given(httpClient.execute()).willReturn(httpResponse);
        given(httpResponse.body()).willThrow(HttpException.class);

        ValueOperations valueOperationsMock = mock(ValueOperations.class);
        given(stringRedisTemplate.opsForValue()).willReturn(valueOperationsMock);
        given(valueOperationsMock.get(anyString())).willReturn("34343");

        // 调用目标方法
        ClaimBankAccountAntiResDTO response = erpAdaptor.claimBankAccount(antiReqDTO);

        // 验证返回结果
        assertNotNull(response);
        assertFalse(response.getResult());
        assertEquals("接口调用异常", response.getMessage());
    }
    //endregion
}
复制代码

 

 

断言

私有方法未被调用

     AwardPunishmentApplicationServiceImpl spyService = PowerMockito.spy(
            awardPunishmentApplicationService);
        // 执行测试
        spyService.auditPunishmentStatus(command, operateContext);
        // 验证:不检查结算日
        verifyPrivate(spyService, never()).invoke("checkSettlementDateExceeded", anyList());

 

 

posted @   意犹未尽  阅读(39)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
历史上的今天:
2019-06-11 RabbitMQ-rabbitmqctl多机多节点和单机多节点集群搭建(五)
点击右上角即可分享
微信分享提示