Mockito,PowerMock结合Unit 示例

Unit test 示例

​ 单元测试选用的测试框架junit,如果依赖Spring 框架需要Mock 数据,选用mockitopowermock

  1. mockito的工作原理是通过创建依赖对象的proxy,所有的调用先经过proxy对象,proxy对象拦截了所有的请求再根据预设的返回值进行处理。mockito 可以mock Spring 依赖的bean。适用需要创建实例的对象。

  2. powerMock则在mockito原有的基础上做了扩展,通过修改类字节码并使用自定义ClassLoader加载运行的方式来实现mock静态方法、final方法、private方法、系统类的功能。

  3. 依赖的pom可以参考:

            <dependency>
                <groupId>org.mockito</groupId>
                <artifactId>mockito-core</artifactId>
    		  <version>1.10.19</version>
            </dependency>
            <dependency>
                <groupId>org.powermock</groupId>
                <artifactId>powermock-core</artifactId>
                <version>1.6.2</version>
            </dependency>
            <dependency>
                <groupId>org.powermock</groupId>
                <artifactId>powermock-module-junit4</artifactId>
                <version>1.6.2</version>
            </dependency>
            <dependency>
                <groupId>org.powermock</groupId>
                <artifactId>powermock-api-mockito</artifactId>
                <version>1.6.2</version>
            </dependency>
    
1. 工具类测试用例

形如工具类,所有的方法不依赖其他的bean参与,仅仅是逻辑处理,不需要mock任何数据,仅Junit足矣处理。

    @Test
    public void putItemsInListBoxTest() {
        val rtn = WorkflowHelper.getProcedure(TaskSetWorkflow.class);
        assertFalse(rtn.getNodes().isEmpty());
    }
2. 依赖spring容器注入的bean

mockito 可以mock依赖其他bean所处理的数据。

  1. @InjectMocks创建的Mock对象BagTaskIndexService可以直接调用真实的代码。
  2. @Mock创建的对象将被注入到BagTaskIndexService中所有的bean。正因为这个特性。@InjectMock修饰被测试类,@Mock修饰其依赖。
  3. @Spy修饰的成员变量调用时,默认是进行真实调用,但也可以Mock调用。
  4. 执行单元测试逻辑,需要预先mock 对应的数据,简称插桩,格式形如when(@Mock bean call method).then(Mock data)

可以参考以下示例:

// MockitoJUnitRunner 框架
@RunWith(MockitoJUnitRunner.class)
// 忽略一些依赖初始化的东西
@PowerMockIgnore("javax.management.*")
public class BagTaskIndexServiceTest {

    //InjectMocks 代表测试需要执行的bean
    @InjectMocks
    private BagTaskIndexService bagTaskIndexService;
    //Mock 待测试执行bean 所依赖的其他bean
    @Mock
    private BagTaskInfoDao bagTaskInfoDao;
    @Mock
    private BagTaskDao bagTaskDao;
    @Mock
    private BagTaskContentsDao contentsDao;
    @Mock
    private ContentSegmentationDao segmentationDao;
    @Mock
    private EsQconfigListener listener;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
    }
    @Test()
    public void pushFullEntityToQmq() throws Exception {
        when(bagTaskInfoDao.queryAll(0L, 1000)).thenReturn(mockGetTaskInfos());
        when(bagTaskDao.queryByPk(1L)).thenReturn(mockGetTask(1L));
        when(contentsDao.queryByInfoIdIn(Arrays.asList(1L))).thenReturn(mockGetTaskContents());
        when(segmentationDao.findByBizIdAndBizTable(1L, 1)).thenReturn(mockGetSegments());
        when(listener.getIdcList(bagTaskIndexService.getClusterName().name())).thenReturn(Lists.newArrayList());
        when(bagTaskIndexService.sendMessage(Mockito.any(), bagTaskIndexService.getFullTopic())).thenAnswer(new Answer<Object>() {
            @Override
            public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
                return null;
            }
        });
        try {
            bagTaskIndexService.pushFullEntityToQmq("tuma_bag_task2020-04-26");
        } catch (IllegalArgumentException e) {
            Assert.assertTrue(e instanceof IllegalArgumentException);
        }


    }

    private Optional<ContentSegmentation> mockGetSegments() {
        return Optional.empty();
    }

    private List<BagTaskContents> mockGetTaskContents() {
        BagTaskContents content = new BagTaskContents();
        content.setId(1L);
        content.setBagTaskInfoId(1L);
        content.setValue("Text");
        content.setLocale("zh-CN");
        content.setEditor(-1);
        return Arrays.asList(content);
    }

    private List<BagTaskInfo> mockGetTaskInfos() {
        List<BagTaskInfo> bagTaskInfos = Lists.newArrayList();
        BagTaskInfo info = new BagTaskInfo();
        info.setId(1L);
        info.setBagTaskId(1L);
        info.setResourceType("test");
        info.setResourceKey("1234");
        bagTaskInfos.add(info);
        return bagTaskInfos;

    }

    private BagTask mockGetTask(Long taskId) {
        BagTask bagTask = new BagTask();
        bagTask.setId(taskId);
        bagTask.setName("test");
        return bagTask;
    }
}
3. 依赖Spring容器bean同时依赖静态类
  1. mockito 可以mock依赖其他bean所处理的数据。
  2. powermock可以mock静态方法、final方法、private方法、系统类的功能。

mockito 参考 2. 依赖spring容器注入的bean

powermock 静态方法,参考如下步骤:

	1. 指定所需静态类 `@PrepareForTest(BagTaskHelper.class)`。
	2. 初始化 测试类型所需的静态类 `PowerMockito.mockStatic(BagTaskHelper.class)`。
            		3. `mock`对应的数据,`PowerMockito.when(BagTaskHelper.getTask(26L)).thenReturn(task);`.
// MockitoJUnitRunner 框架
@RunWith(PowerMockRunner.class)
// 忽略一些依赖初始化的东西
@PowerMockIgnore("javax.management.*")
public class BagTranslationLogServiceTest {

    // 需要注入的bean
    @InjectMocks
    private BagTranslationLogService bagTranslationLogService;
    // 需要注入的bean而依赖的bean 即为InjectMocks 所有依赖的bean
    @Mock
    private BagTaskTranslationHiveDao hiveDao;
    // 静态类型所需要的类型
    @PrepareForTest(BagTaskHelper.class)
    @Test
    public void query() throws IOException, InvocationTargetException, IllegalAccessException {
        List<BagTaskTranslationHiveEntity> logEntities = mockHiveEntities();
        // mock bean method 返回的数据
        when(hiveDao.scanLogEntities(26L, "1235", "zh-CN", null, null, 1000)).thenReturn(logEntities);
        BagTask task = new BagTask();
        task.setName("test");
        // Power mock static 方法数据
        PowerMockito.mockStatic(BagTaskHelper.class);
        PowerMockito.when(BagTaskHelper.getTask(26L)).thenReturn(task);
        List<Map<String, Object>> objects = bagTranslationLogService.query(26L, "1235", "zh-CN", null, null);
        Assert.assertTrue(!objects.isEmpty() && objects.get(0).size() == 4);

    }

    private List<BagTaskTranslationHiveEntity> mockHiveEntities() {
        List<BagTaskTranslationHiveEntity> logEntities = Lists.newArrayList();
        BagTaskTranslationHiveEntity entity = new BagTaskTranslationHiveEntity();
        entity.setBagTaskId(26L);
        entity.setLocale("zh-CN");
        entity.setOperator("test");
        entity.setReferenceLocale("en-Us");
        entity.setReferenceText("test");
        entity.setResourceKey("1235");
        entity.setResourceType("test");
        entity.setStatus("Add");
        entity.setTranslation("test");
        entity.setTimeStamp(new Timestamp(System.currentTimeMillis()));
        logEntities.add(entity);
        return logEntities;
    }
}
4. Mock各种实际情况
1)忽略静态变量初始化

@SuppressStaticInitializationFor({"com.ctrip.ibu.flit.boot.utils.XX"}) 指定当前类所有的静态变量和静态块都不会初始化。

2)初始化静态变量

Whitebox.setInternalState(XX.class, "FIELD_NAME", VALUE); 针对忽略静态块和变量初始化,但是部分变量需要初始化。

3)依赖的beanlist 或者map
a) beanlist
    @Spy
    List<ISentenceTrans> flitLegacyTransList = Lists.newArrayList();
    @Mock
    private EchoTrans echoTrans;

    @Before
    public void init() {
        MockitoAnnotations.initMocks(this);
        flitLegacyTransList.add(echoTrans);
    }
b)beanmap

    @Mock
    private EnRoomNameProcessor processor;
    @Spy
    private Map<String, RoomTypeProcessorChain> roomTypeProcessorChain = Maps.newHashMap();

    @Before
    public void init() {
        MockitoAnnotations.initMocks(this);
        roomTypeProcessorChain.put("OVERSEA", new RoomTypeProcessorChain(Arrays.asList(processor)));
        roomTypeProcessorChain.put("DOMESTIC", new RoomTypeProcessorChain(Arrays.asList(processor)));
        roomTypeProcessorChain.put("CN_TC_TW", new RoomTypeProcessorChain(Arrays.asList(processor)));
        roomTypeProcessorChain.put("NORMAL", new RoomTypeProcessorChain(Arrays.asList(processor)));
    }
4) 链式调用mock 深度插桩

​ 需要注意的是,只有Mockito才支持深度插桩,PowerMock是不支持的,指定mock参数RETURNS_DEEP_STUBS

@RunWith(MockitoJUnitRunner.class)
public class RefundGoTranslatorTest {


    @Test
    public void getTranslatorName() {
        RefundGoTranslator changeBackTranslator = Mockito.mock(RefundGoTranslator.class, RETURNS_DEEP_STUBS);
        Assert.assertEquals(changeBackTranslator.translate(any()).getProgress(), Progress.COMPLETED);
    }
}

建议

编写所有的method,尽量都有返回值,因为如果过没有返回值,就没有办法mock数据。比如说你想跳过流程中某个函数,虽然你可以doNothing().when(T).dothing(),第一次起作用,但是第二次运行会有问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值