Mockito使用问题合集

1.mock泛型方法

  • 反射获取Method时,泛型类型使用 Object.class 替换。
  • 调用方法时,泛型入参,不能使用new Object(), 需要使用具体的实现类。

1.1 源代码

public abstract class Test<OUT> {
    
    private void setOutPut(OUT model, Map<String, String> params) {
            // do something
    }
}

public class TestSub extends Test<TestSubDO>{
    // do something
}

1.2 mock代码

@RunWith(MockitoJUnitRunner.class)
public class TestTest {
    @InjectMock
    private Test test = new TestSub();
    
    @Test
    public void test1(){
        // OUT 使用 Object.class 替换
        Method method = Test.class.getDeclaredMethod("setOutPut", Object.class, Map.class);
        method.setAccessible(true);
        // TestSubDO
        method.invoke(test, new TestSubDO(), new HashMap());
    }
}

2.循环中多次调用同一方法

在一个循环中多次调用同一个有返回值的方法,模拟多次返回值,需要多次使用thenReturn方法,或在thenReturn中设置多个值。

2.1源代码

while(true){
    Page<TestPO> pageList = testMapper.selectPage(pageQuery);
    if(CollectionUtils.isEmpty(pageList.getData())) {
        break;    
    }
    // todo something 
}

2.2 mock代码

Page<TestPO> page1=new Page(1,10,1,Lists.newArrayList(new TestPO()));
Page<TestPO> page2=new Page(1,10,1,Collections.emptyList());
// 第二次调用返回数据为空,则会退出while循环
when(testMapper.selectPage(any())).thenReturn(page1).thenReturn(page2);

3.MockitoException for Static Mocks异常

在test/resources文件夹下追加:

  1. 新建文件夹 mockito-extensions
  2. 在新的文件夹中新建文本文件 org.mockito.plugins.MockMaker
  3. 向文本中写入内容:mock-maker-inline

4.模拟protected方法

有2种方式可以模拟protected方法。

4.1方式1使用ReflectionSupport

ReflectionSupport类是JUnit5提供的一个类。可以使用 invokeMethod方法,执行protected方法。

public static Object 
invokeMethod(Method method, Object target, Object ...args)

4.2方式2使用Answers.CALLS_REAL_METHODS

注意:这种方式时,需要将xxxTest.java的package路径和xxx.java类的package路径保持一致,不一致时调用protected方法时报错。

Abstarct ab = mock(Abstarct.class, Answers.CALLS_REAL_METHODS);
ab.protectedMethod();

5.mybatis-plus相关问题

5.1 MyBatisPlusException:can not find lambda cache for this entity

解决方法是在单测方法第一行加入一行代码:

TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), "")
, TestPO.class);

注意:TestPO是数据库表对应的实体类。

6.模拟EasyExcel读取数据

6.1源代码

读取Excel文件时使用以下方法:

EasyExcel.read(String filePath, Class head, ReadListener listener);

源代码应用:

@Data
public class TestDO {
    @ExcelProperty(value = "名称", index = 0)
    private String name;
}

public class ReadTestListener extend AnalysisEventListener<TestDO> {

    private List<TestDO> dataList = new ArrayList<>();
    public List<TestDO> getDataList() {
        return this.dataList;
    }

    // 覆写方法 invoke, doAfterAllAnalysed 
}

// 后面要mock的方法
public void readExcel(String filePath) {
    ReadTestListener listener = new ReadTestListener();
    EasyExcel.read(filePath, TestDO.class, listener).sheet().doRead();
}

6.2 mock代码

@Test
public void test1() {
    String filepath = "c:\\testdata\testDo.xlsx";
    ExcelReaderBuilder readerBuilder = Mockito.mock(ExcelReaderBuilder.class);
    try(MockedStatic mockEasyExcel = Mockito.mockStatic(EasyExcelFactory.class, invocation -> {
        if (invocation.getMethod().getName().equals("read")) {
            return readerBuilder;        
        }    
        return null;
    })) {
            ExcelReaderSheetBuilder excelReaderSheetBuilder=mock(ExcelReaderSheetBuilder.class);
            when(readerBuilder.sheet()).thenReturn(excelReaderSheetBuilder);
            doNothing().when(excelReaderSheetBuilder).doRead();
            
            // 调用mock的方法
            readExcel(filepath);
    }
}

后续遇到问题会继续补充。