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());
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
2019-06-11 RabbitMQ-rabbitmqctl多机多节点和单机多节点集群搭建(五)