0


Spring 应用进行Mockito 单元测试详解

个人理解

通过mockito给程序设定一个预期值,然后通过mockito仿真执行程序,看执行逻辑输出是否符合预期的结果。主要用于检测逻辑是否正确。由于不是真的执行,因此会隔离真实环境。无法测试底层调用或者sql是否存在问题。

mockito 资源

官网: http://mockito.org
API文档:http://docs.mockito.googlecode.com/hg/org/mockito/Mockito.html
项目源码:https://github.com/mockito/mockito

依赖

<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><dependency><groupId>org.mockito</groupId><artifactId>mockito-all</artifactId><version>1.10.19</version><scope>test</scope></dependency><dependency><groupId>org.mockito</groupId><artifactId>mockito-core</artifactId><version>3.3.3</version><scope>test</scope></dependency><dependency><groupId>org.powermock</groupId><artifactId>powermock-module-junit4</artifactId><version>1.6.5</version><scope>test</scope></dependency><dependency><groupId>org.powermock</groupId><artifactId>powermock-api-mockito</artifactId><version>1.6.5</version><scope>test</scope></dependency>

注解

注解作用例子@PowerMockIgnore忽略一些模块@PowerMockIgnore(“javax.management.*”)@PrepareForTestmock静态类@PrepareForTest({NumberUtils.class})@RunWith启动注解,使用什么来运行程序@RunWith(MockitoJUnitRunner.class)

注意事项

  • @PowerMockIgnore(“javax.management.*”)

由于PowerMock的工做原理便是使用自定义的类加载器来加载被修改过的类,从而达到打桩的目的,使用Powermock后会提示classloader错误,所以待测试类中使用到了XML解析相关的包和类,那么测试类前一样须要增长@PowerMockIgnore({“org.xml.*", "javax.xml.*”}),消除类加载器引入报错。

  • @PrepareForTest({NumberUtils.class})

把静态方法mock掉,模拟调用静态方法,返回一个给定的值。

PowerMockito.mockStatic(NumberUtils.class);when(NumberUtils.change()).thenReturn("123");
  • 调用无返回的方法 PowerMockito.doNothing().when(casService).addSupplier(anyLong(), any(ServiceKey.class));

连续调用

@Test(expected =RuntimeException.class)publicvoidcontinuousCallTest(){// 模拟连续调用返回指望值,若是分开,则只有最后一个有效PowerMockito.when(exampleServiceMock.aTest()).thenReturn("1");PowerMockito.when(exampleServiceMock.aTest()).thenReturn("2");PowerMockito.when(exampleServiceMock.aTest()).thenReturn("3");PowerMockito.when(exampleServiceMock.aTest()).thenReturn("4");PowerMockito.when(exampleServiceMock.aTest()).thenReturn("1").thenReturn("2").thenThrow(newRuntimeException());Assert.assertEquals("1", exampleServiceMock.aTest());Assert.assertEquals("2", exampleServiceMock.aTest());Assert.assertEquals("3", exampleServiceMock.aTest());Assert.assertEquals("4", exampleServiceMock.aTest());// 第三次或更多调用都会抛出异常
    exampleServiceMock.aTest();}

ExampleServiceImplTest

importcom.jd.work.example.service.ExampleService;importcom.jd.work.example.utils.RedisCache;importlombok.extern.slf4j.Slf4j;importorg.junit.Assert;importorg.junit.Before;importorg.junit.Test;importorg.junit.runner.RunWith;importorg.mockito.InjectMocks;importorg.mockito.Mock;importorg.mockito.MockitoAnnotations;importorg.mockito.runners.MockitoJUnitRunner;importorg.powermock.api.mockito.PowerMockito;importorg.powermock.core.classloader.annotations.PowerMockIgnore;importorg.powermock.core.classloader.annotations.PrepareForTest;@Slf4j@RunWith(MockitoJUnitRunner.class)//@RunWith(PowerMockRunner.class)@PrepareForTest()@PowerMockIgnore("javax.management.*")publicclassExampleServiceImplTest{/**
     * 待测试的具体实现类
     */@InjectMocksprivateExampleServiceImpl exampleService;@MockprivateRedisCache redisCache;/**
     * 调用了自身接口的其他方法
     */@MockprivateExampleService exampleServiceMock;@BeforepublicvoidsetUp(){// mock注解初始化,不加会报错MockitoAnnotations.initMocks(this);}@Testpublicvoidexample(){PowerMockito.when(exampleServiceMock.bTest()).thenReturn("ok-b");String s = exampleService.aTest();Assert.assertEquals("ok-b", s);}@Test(expected =RuntimeException.class)publicvoidcontinuousCallTest(){// 模拟连续调用返回指望值,若是分开,则只有最后一个有效PowerMockito.when(exampleServiceMock.aTest()).thenReturn("1");PowerMockito.when(exampleServiceMock.aTest()).thenReturn("2");PowerMockito.when(exampleServiceMock.aTest()).thenReturn("3");PowerMockito.when(exampleServiceMock.aTest()).thenReturn("4");PowerMockito.when(exampleServiceMock.aTest()).thenReturn("1").thenReturn("2").thenThrow(newRuntimeException());Assert.assertEquals("1", exampleServiceMock.aTest());Assert.assertEquals("2", exampleServiceMock.aTest());Assert.assertEquals("3", exampleServiceMock.aTest());Assert.assertEquals("4", exampleServiceMock.aTest());// 第三次或更多调用都会抛出异常
        exampleServiceMock.aTest();}@MockprivateList<String> list;@Testpublicvoidtest(){
        list.add("test");verify(list).add("test");verify(list,atLeastOnce()).add("test");verify(list,atLeast(1)).add("test");verify(list,atMost(2)).add("test");verify(list,never()).add("test111");assertThat(0,equalTo(list.size()));}@Testpublicvoidtest3(){
        list.add("test");verify(list).add("test");// open will fail// list.clear();// 代表上一次verify之后再无与list的交互verifyNoMoreInteractions(list);}@Testpublicvoidtest4(){
        list.add("test");// 自始至终都与list无任何交互verifyZeroInteractions(list);}}

Mock静态方法

测试类头部需要加

@PrepareForTest({StaticClass.class})
@RunWith(PowerMockRunner.class)@PrepareForTest({StaticClass.class})publicclassExampleTest{@Testpublicvoidtest(){PowerMockito.mockStatic(StaticClass.class);}}

Mock私有方法1

@RunWith(PowerMockRunner.class)@PowerMockIgnore("javax.management.*")publicclassExampleTest{@InjectMocksprivateExampleServiceImpl exampleService;@TestpublicvoidtestPrivateMethod()throwsNoSuchMethodException,InvocationTargetException,IllegalAccessException{Class<?>[] params =newClass<?>[]{String.class,Map.class};Map<String,Object> paramsMap =newHashMap<>(3);
        paramsMap.put("asf_id","123");
        paramsMap.put("asf_urge_time","22222");
        paramsMap.put("asf_apply_time","24141211");Method method = exampleService.getClass().getDeclaredMethod("handleMessageTemplate", params);//making private method accessible
        method.setAccessible(true);assertEquals("ASF_URGE_SECOND_TEMPLATE_DEFAULT", method.invoke(exampleService,"ASF_URGE_SECOND_TEMPLATE_DEFAULT", paramsMap));

        method.invoke(exampleService,"", paramsMap);}}

Mock无返回方法1

@RunWith(PowerMockRunner.class)@PowerMockIgnore("javax.management.*")publicclassExampleTest{@MockprivateRedisCache redisCache;@TestpublicvoidmethodNoReturnTest(){PowerMockito.doAnswer(invocation ->{Object[] args = invocation.getArguments();return"called with arguments: "+Arrays.toString(args);}).when(redisCache).zAdd("key","xx",30);}}

Mock抛出异常

// 有返回值抛异常Mockito.when(mockitoTestModel.returnString()).thenThrow(newMyException());// 无返回值抛异常Mockito.doThrow(newMyException("TEST")).when(mockitoTestModel).noReturn();

Mock私有方法2

// 私有方法测试,无参 Method method =PowerMockito.method(TestService.class,"methodName");
method.invoke(testService);// 私有方法测试,传参Method method =PowerMockito.method(TestService.class,"methodName",Test1.class,Test2.class);
method.invoke(testService, test1, test2);
@TestpublicvoidtestPrivateMethod2(){try{Map<String,Object> paramsMap =newHashMap<>(3);
        paramsMap.put("asf_id","123");
        paramsMap.put("asf_urge_time","22222");
        paramsMap.put("asf_apply_time","24141211");Method method =PowerMockito.method(ExampleServiceImpl.class,"handleAnnotation",String.class,Map.class);
        method.invoke(exampleService,"ASF_URGE_SECOND_TEMPLATE_DEFAULT", paramsMap);//making private method accessibleassertEquals("ASF_URGE_SECOND_TEMPLATE_DEFAULT", method.invoke(exampleService,"ASF_URGE_SECOND_TEMPLATE_DEFAULT", paramsMap));

        method.invoke(exampleService,"", paramsMap);}catch(IllegalAccessException|IllegalArgumentException|InvocationTargetException e){
        e.printStackTrace();}}

Mock无返回方法2

@TestpublicvoidwhenAddCalledVerfied(){List myList =mock(List.class);Mockito.doNothing().when(myList).add(isA(Integer.class),isA(String.class));
    myList.add(0,"");verify(myList,times(1)).add(0,"");}

Mock父类

// 当前正在测试ExampleServiceImpl的example方法,ExampleServiceImpl继承BaseServiceImpl类,从而获得一下公共的通用方法。// 其中getCurrent()为私有方法,getUser()为protected方法。// ExampleServiceImpl的example方法调用了getUser方法。BaseServiceImpl underTest =spy(BaseServiceImpl.class);LoginContext loginContext =newLoginContext();
loginContext.setUser("1233");
underTest.setCurrent(loginContext);

本文转载自: https://blog.csdn.net/qq_24142271/article/details/125682731
版权归原作者 jockming 所有, 如有侵权,请联系我们删除。

“Spring 应用进行Mockito 单元测试详解”的评论:

还没有评论