使用Mockito实现单元测试

目录

1.常用注解

@RunWith

@Test

[@Before 和 @After](#@Before 和 @After)

@Mock

@Spy

@Captor

@InjectMocks

2.Mockito常用方法

[Mockito.any(Class type)](#Mockito.any(Class type))

Mockito.eq()

Mockito.when()

[Mockito.mock(Class classToMock)](#Mockito.mock(Class classToMock))

Mockito.doReturn()

Mockito.verify()

Mockito.reset

[mock 异常和void处理:](#mock 异常和void处理:)


Mockito 是一个流行的 Java 单元测试框架,用于模拟(mock)对象以便进行单元测试。它可以帮助开发人员创建和管理模拟对象,以便在测试过程中替换那些不容易构造或获取的对象,从而达到测试被测代码的行为,而无需依赖于实际的外部系统或服务。

相信很多公司都不会做单元测试,觉得耗时且收益低,随着AI辅助工具流行,通过AI工具可以很简答的实现单元测试。 例如图中通过通义灵码帮助完成单元测试代码,在项目降低了程序中的缺陷,而单元测试花费时间大幅度下降。在一定程度上提高了开发人员投入产生的收益。这也许是AI对软件工程最直观的影响

Mockito 的主要特点包括:

  • 模拟对象:Mockito 允许创建模拟对象,这些模拟对象具有与真实对象相似的行为,但实际上只是虚拟的对象实例。通过模拟对象,可以模拟外部依赖、交互行为等,从而使测试更加独立和可控。
  • 验证行为:Mockito 提供了丰富的 API 来验证模拟对象的交互行为,例如方法是否被调用、调用次数、参数匹配等。通过验证行为,可以确保被测代码按预期执行了与外部系统的交互。
  • Stubbing 方法:Mockito 允许对模拟对象的方法进行 Stubbing,即定义当调用某个方法时应该返回的值。这样可以模拟不同的场景和条件,以覆盖多种测试情况。
  • 灵活性:Mockito 提供了简洁且易于使用的 API,支持与 JUnit、TestNG 等测试框架集成。同时,它还提供了丰富的功能和扩展,可以满足各种测试场景的需求。

Mockito依赖

|-----------------------------------------------------------------------------------------------------------------------------------------------------------|
| testImplementation group: 'junit', name: 'junit', version: '4.12' testImplementation group: 'org.mockito', name: 'mockito-core', version: '3.1.0' |

1.常用注解

@RunWith

@RunWith注解用于在JUnit测试中指定运行器(Runner),常用的有以下两种

@RunWith(MockitoJUnitRunner.class):

@RunWith(MockitoJUnitRunner.class) 是Mockito框架提供的JUnit运行器,用于运行使用Mockito进行单元测试的类。

当使用 @RunWith(MockitoJUnitRunner.class) 注解一个测试类时,Mockito会自动初始化这个类中使用 @Mock、@InjectMocks 等注解创建的模拟对象。

这样可以简化测试类的设置过程,使得在测试中可以直接使用模拟对象而不需要手动初始化。

|--------------------------------------------------------------------------------------------|
| @RunWith(MockitoJUnitRunner.class) public class ProductBillStartDateGetterTest { } |

@Test

标记测试方法,被 @Test 注解标记的方法会被 JUnit 的运行器执行,作为测试方法进行测试。

|--------------------------------------------------------------------------------|
| @Test public void getBillStartDate_StrategyIsNull_nullStrategy() {   } |

@Before 和 @After

JUnit 提供的注解,在每个测试方法执行之前和之后分别执行一段代码。通常用于设置测试的前置条件或进行资源的清理工作。

|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| @Before public void setUp() { mockAction = new Action(); mockAction.setActionType(mock(ActionType.class)); purchaseDT =new Date(); productBillStartDateGetter = new ProductBillStartDateGetter(purchaseDT, businessBillService, strategyService); } |

@Mock

创建一个模拟对象,并注入到测试类中。通常和 @InjectMocks 注解一起使用,用于为测试类中的被测对象注入模拟对象。当你使用 @Mock 注解一个对象时,Mockito 会为你创建一个该对象的模拟实例。这个模拟实例的行为(即方法的返回值和它们被调用的方式)可以被精确地控制和验证。默认情况下,模拟对象上的所有方法调用都会返回 null(对于引用类型)或默认值(对于原始类型)。你可以使用 when(...).thenReturn(...)、when(...).thenThrow(...) 等 Mockito 方法来定义模拟对象的行为。

@Spy

用于创建一个部分模拟对象,与@Mock不同,@Spy创建的是一个实际对象的代理,并且允许你选择性地模拟对象中的某些方法。默认情况下,所有未被特别模拟的方法都会调用实际对象的方法,

通常与doReturn/doThrow等方法结合使用。

@Captor

用于捕获方法参数,以便验证方法调用参数是否正确。

@InjectMocks

注入被测对象,并自动将 @Mock 和 @Spy 标注的对象注入到被测对象中。

|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| @RunWith(MockitoJUnitRunner.class) public class ServiceContextTest { @Mock private IProdProvExService prodProvExService; @Mock private OpenDeviceHelper openDeviceHelper; @InjectMocks private ServiceContext serviceContext; } |

2.Mockito常用方法

Mockito.any(Class type)

Mockito.any(Class type) 用于创建一个匹配任意指定类型的参数的匹配器。在设置模拟对象方法的行为时,我们经常会使用 Mockito.any() 来匹配任意参数。

下面是 Mockito.any() 方法的一些常见用法:

Mockito.eq()

Mockito.eq() 用于创建一个匹配特定参数值的匹配器。当你想在模拟对象的方法调用中匹配一个特定的参数时,你可以使用 eq。它通常与 when 和 verify 等方法一起使用。

Mockito.when()

Mockito.when() 是 Mockito 框架中的一个方法,用于设置模拟对象的方法调用行为。它通常与 thenReturn()、thenThrow() 或 thenAnswer() 方法一起使用。

Mockito.when(mock.methodCall()).thenReturn(value) 的语法表示当模拟对象的 methodCall() 方法被调用时,应该返回指定的 value 值。

|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| //1.返回空 when(strategyService.locateProductBillingEntitleDateStrategy(anyLong(), anyLong(), eq(StrategyType.BeginChargeDate))).thenReturn(null); //2.返回1个值 Strategy strategy = new Strategy(); strategy.setStrategyDetail(StrategyDetail.OrderDoneDateBegin); when(strategyService.locateProductBillingEntitleDateStrategy(anyLong(), anyLong(), eq(StrategyType.BeginChargeDate))).thenReturn(strategy); //3.第一次返回strategy1和第2次返回strategy2 when(strategyService.locateProductBillingEntitleDateStrategy(anyLong(), anyLong(), eq(StrategyType.BeginChargeDate))).thenReturn(strategy1).thenReturn(strategy2); //4.返回集合有1个值 when(subscriberExtInfoDao.querySubscriberExtInfoByCondition(any())).thenReturn(Collections.singletonList(subscriberAddon)); //5.返回空集合 when(orderItemContext.getSubscriberContexts()).thenReturn(Collections.emptyList()); |

Mockito.mock(Class classToMock)

用于创建一个指定类的模拟对象

|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| @Test public void getBillStartDate_StrategyIsNull_nullStrategy() { when(strategyService.locateProductBillingEntitleDateStrategy(anyLong(), anyLong(), eq(StrategyType.BeginChargeDate))).thenReturn(null); AbstractProduct product = mock(AbstractProduct.class); Date result = productBillStartDateGetter.getBillStartDate(product, mockAction); assertEquals(DateUtils.getBeginOfDate(purchaseDT), result); } |

Mockito.doReturn()

Mockito.doReturn() 用于设置模拟对象方法的返回值。与 Mockito.when().thenReturn() 方法类似,Mockito.doReturn() 也用于模拟方法调用时的返回结果,但在某些特定情况下会更加灵活和有效。

以下是 Mockito.doReturn() 方法的一些常见用法:

设置模拟对象方法的返回值:

|----------------------------------------------------------------------------------------------------------------------------|
| // 使用 doReturn() 设置 getUserById() 方法的返回值 Mockito.doReturn(new User("Alice")).when(userServiceMock).getUserById(1); |

Mockito.verify()

Mockito.verify(): 用于验证模拟对象的方法是否被调用以及调用的次数。它通常与 times()、atLeastOnce()、never() 等方法一起使用。

Mockito.verify(mock, times(n)).methodCall() 的语法表示验证模拟对象的 methodCall() 方法被调用了 n 次。

|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| @Test public void getBillStartDate_StrategyDetailOrderDoneDateBegin_OrderDoneDateBegin() { Strategy strategy = new Strategy(); strategy.setStrategyDetail(StrategyDetail.OrderDoneDateBegin); when(strategyService.locateProductBillingEntitleDateStrategy(anyLong(), anyLong(), eq(StrategyType.BeginChargeDate))).thenReturn(strategy); AbstractProduct product = mock(AbstractProduct.class); Date result = productBillStartDateGetter.getBillStartDate(product, mockAction); assertEquals(DateUtils.getBeginOfDate(purchaseDT), result); Mockito.verify(businessBillService, never()).getInvoiceCyc(any(Date.class),anyInt()); Mockito.verify(strategyService, times(1)).locateProductBillingEntitleDateStrategy(anyLong(), anyLong(), any(StrategyType.class)); Mockito.verify(strategyService).locateProductBillingEntitleDateStrategy(anyLong(), anyLong(), any(StrategyType.class)); } |

Mockito.reset

Mockito.reset(mock):重置模拟对象的状态,清除之前的交互和验证。

mock 异常和void处理:

|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| // 设置 delete() 方法为无返回值时的行为 Mockito.doNothing().when(userServiceMock).deleteUser(1); // 设置 void 方法抛出异常时的行为 Mockito.doThrow(new RuntimeException("Unable to delete user")).when(userServiceMock).deleteUser(2); Mockito.when(userServiceMock.saveUser(null)).thenThrow(new IllegalArgumentException("Invalid parameter")); //断言异常 assertThrows(MsrRuntimeException.class, () -> cmResourceChecker.checkHaveCmSubscriberAddon(orderItemContext)); |

相关推荐
Seven979 分钟前
【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
java·后端·设计模式
matrixlzp12 分钟前
Java 生成图形验证码
java·spring
冰淇淋@13 分钟前
HTTP发送POST请求的两种方式
java·spring boot·http
codingPower28 分钟前
IDEA接入阿里云百炼中免费的通义千问[2025版]
java·阿里云·intellij-idea
JouJz1 小时前
Java基础系列:深入解析反射机制与代理模式及避坑指南
java·开发语言·代理模式
小安同学iter1 小时前
Spring(七)AOP-代理模式
java·后端·spring
鸽鸽程序猿1 小时前
【JavaEE】SpringIoC与SpringDI
java·开发语言·java-ee
Yuanymoon2 小时前
【由技及道】API契约的量子纠缠术:响应封装的十一维通信协议【人工智障AI2077的开发日志012】
java·架构设计·spirng
lucky1_1star2 小时前
FX-函数重载、重写(覆盖)、隐藏
java·c++·算法
李长渊哦3 小时前
学习文章:Spring Boot 中如何使用 `@Async` 实现异步处理
java·spring boot·学习