mockito 的一些使用经验

188 阅读3分钟

前言

在分布式系统开发或多人复杂业务开发过程中,我们经常需要调用其他人的服务或方法而又不需要关注服务的实现,但在开发过程中为了不相互阻塞我们都会先定义好接口规范各自开发各自的业务实现。这时mocktio 就发了它的作用。

mock 静态类

MockedStatic<MessageResource>  mockStaticMessageResource= Mockito.mockStatic(MessageResource.class);
​
ps: 静态类需要释放,要么用try 包装起来 ,要么关闭时调用close() 关闭
​
    1.
    try(MockedStatic<MessageResource>  mockStaticMessageResource= Mockito.mockStatic(MessageResource.class)){
             when(MessageResource.getMessage(Mockito.any())).thenReturn("test")
    }
   
   2. BeforeEach 初始化 AfterEach关闭
   private MockedStatic<MessageResource> messageResource;
​
   @BeforeEach
    public void before() {
        MockitoAnnotations.openMocks(this);
        messageResource = Mockito.mockStatic(MessageResource.class);
        messageResource.when(() -> MessageResource.getMessage(Mockito.any())).thenReturn("test");
    }
​
    @AfterEach
    public void after() {
        messageResource.close();
    }

调用方法内部私有方法

@InjectMocks
private InteractiveLessonFinishMessageHandler handler;
​
InteractiveLessonFinishMessageHandler serviceSpy = spy(handler);
//this 改变this指向
ReflectionTestUtils.setField(this, "handler", serviceSpy);

反射设置属性

  1. Spring ReflectionTestUtils 赋值
    ReflectionTestUtils.setField(taskService, "finishLessonAfterDay", 60);
    
  2. Mockito Whitebox 利用反射,为字段赋默认值
        // 需要mock的类中,有很多依赖读取外部属性。 我们在这里使用Whitebox.setInternalState,为mock的类 字段属性给默认值,走完mock
     Whitebox.setInternalState(classResOperateHandler, "defaultPageSize", 200);
    

调用私有方法

  1. 使用spring ReflectionTestUtils
    org.springframework.test.util.ReflectionTestUtils#invokeMethod
    ​
    示例:
    ReflectionTestUtils.invokeMethod(saveUserAnswerRecordAction,"saveLock", caseData.userAnswerRecordVo);   
    
  2. 使用 Mockito Whitebox
     //调用私有方法
     Whitebox.invokeMethod(saveUserAnswerRecordAction,"saveLock", caseData.userAnswerRecordVo);
    

Mybatis-plus 模拟

UserPullMsgRepository userPullMsgRepository = Mockito.mock(UserPullMsgRepository.class);
​
//实体类反射获取表信息,不然Wrappers会报错
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""), UserPullMsg.class);
​
Mockito.doReturn(caseData.dbUserPullMsg).when(userPullMsgRepository)
.selectOne(Mockito.any());

spy 与 mock 辨析

mock 完全代理,spy 部分代理

场景推荐写法注意事项
普通@Mock对象when().thenReturn()等价doReturn
@spydoReturn().when()避免执行真实方法
//对spy对象使用when()会导致真实方法被调用!引发真实调用
when(spyService.method()).thenReturn(resultData);
//正确写法应阻断实际调用,私有方法调用参考 <调用方法内部私有方法>
// @Spy对象必须使用doRetun规避副作用
doReturn(resultData).when(spyService).metho();

when/doReturn辨析

用法

doReturn(): 用于为模拟对象的方法设置返回值,通常与 when() 方法结合使用。例如:

doReturn(10).when(mockObject).getValue();

when(): 除了设置返回值外,还可以指定方法被调用的条件。例如:

when(mockObject.getValue()).thenReturn(10);
关键区别

主要区别在于 when() 方法可以指定调用条件,而 doReturn() 则不能。

  • doReturn(): 无条件地设置返回值。
  • when(): 可以基于方法调用时的特定条件来设置返回值。
何时使用 doReturn()
  • 当不需要设置调用条件时。
  • 当与其他 Mockito 方法(如 doCallRealMethod())结合使用时。
何时使用 when()
  • 当需要指定调用条件时。
  • 当需要使用链式调用来设置多个返回值时。
示例
// doReturn()
MockObject mockObject = mock(MockObject.class);
doReturn(10).when(mockObject).getValue();
int result = mockObject.getValue(); // result = 10// when()
when(mockObject.getValue()).thenReturn(10);
result = mockObject.getValue(); // result = 10
when(mockObject.getValue()).thenReturn(20);
result = mockObject.getValue(); // result = 20

thenReturn() 与 thenAnswer() 辨析

用法
when(list.get(anyInt())).thenReturn(1);
​
doAnswer(inv->{
 reurn 1;
}).when(list).get(anyInt())
区别

thenReturn() 允许在连续调用中返回不同的值。 doAnswer() 可构建没有返回值的方法。

thenReturn() 返回的是一个固定的结果 doAnswer() 可以根据不同的情景返回不同的结果

示例
List<Integer> list=mock(ArrayList.class);
​
//doAnswer() 根据不同的传参,构建不同的结果
 doAnswer(invocation -> {
        Object index = invocation.getArgument(0);
        if(index==1){
           return "answer this 1";
        }
        return "answer this othor";
    }).when(list)
      .get(any(Integer.class));
      
​
//thenReturn 链式调用返回多个值
when(list.get(anyInt()))
      .thenReturn("answer 1")
      .thenReturn("answer 2");
​
assertEquals("answer 1", list.get(1));
assertEquals("answer 2", list.get(1));