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

  • @PrepareForTest({NumberUtils.class})
  • 把静态方法mock掉,模拟调用静态方法,返回一个给定的值。

    PowerMockito.mockStatic(NumberUtils.class);
    NumberUtils numberUtils = PowerMockito.mock(NumberUtils.class);
    when(numberUtils.change()).thenReturn("123");
    
  • 调用无返回的方法
    PowerMockito.doNothing().when(casService).addSupplier(anyLong(), any(ServiceKey.class));
  • @Test(expected = RuntimeException.class)
    public void continuousCallTest() {
        // 模拟连续调用返回指望值,若是分开,则只有最后一个有效
        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(new RuntimeException());
        Assert.assertEquals("1", exampleServiceMock.aTest());
        Assert.assertEquals("2", exampleServiceMock.aTest());
        Assert.assertEquals("3", exampleServiceMock.aTest());
        Assert.assertEquals("4", exampleServiceMock.aTest());
        // 第三次或更多调用都会抛出异常
        exampleServiceMock.aTest();
    

    ExampleServiceImplTest

    import com.jd.work.example.service.ExampleService;
    import com.jd.work.example.utils.RedisCache;
    import lombok.extern.slf4j.Slf4j;
    import org.junit.Assert;
    import org.junit.Before;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.mockito.InjectMocks;
    import org.mockito.Mock;
    import org.mockito.MockitoAnnotations;
    import org.mockito.runners.MockitoJUnitRunner;
    import org.powermock.api.mockito.PowerMockito;
    import org.powermock.core.classloader.annotations.PowerMockIgnore;
    import org.powermock.core.classloader.annotations.PrepareForTest;
    @Slf4j
    @RunWith(MockitoJUnitRunner.class)
    //@RunWith(PowerMockRunner.class)
    @PrepareForTest()
    @PowerMockIgnore("javax.management.*")
    public class ExampleServiceImplTest {
         * 待测试的具体实现类
        @InjectMocks
        private ExampleServiceImpl exampleService;
        @Mock
        private RedisCache redisCache;
         * 调用了自身接口的其他方法
        @Mock
        private ExampleService exampleServiceMock;
        @Before
        public void setUp() {
            // mock注解初始化,不加会报错
            MockitoAnnotations.initMocks(this);
        @Test
        public void example() {
            PowerMockito.when(exampleServiceMock.bTest()).thenReturn("ok-b");
            String s = exampleService.aTest();
            Assert.assertEquals("ok-b", s);
       @Test(expected = RuntimeException.class)
        public void continuousCallTest() {
            // 模拟连续调用返回指望值,若是分开,则只有最后一个有效
            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(new RuntimeException());
            Assert.assertEquals("1", exampleServiceMock.aTest());
            Assert.assertEquals("2", exampleServiceMock.aTest());
            Assert.assertEquals("3", exampleServiceMock.aTest());
            Assert.assertEquals("4", exampleServiceMock.aTest());
            // 第三次或更多调用都会抛出异常
            exampleServiceMock.aTest();
        @Mock
        private List<String> list;
        @Test
        public void test() {
            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()));
        @Test
        public void test3() {
            list.add("test");
            verify(list).add("test");
            // open will fail
            // list.clear();
            // 代表上一次verify之后再无与list的交互
            verifyNoMoreInteractions(list);
        @Test
        public void test4() {
            list.add("test");
            // 自始至终都与list无任何交互
            verifyZeroInteractions(list);
    

    Mock静态方法

    测试类头部需要加@PrepareForTest({StaticClass.class})

    @RunWith(PowerMockRunner.class) @PrepareForTest({StaticClass.class}) public class ExampleTest { @Test public void test() { PowerMockito.mockStatic(StaticClass.class);

    Mock私有方法1

    @RunWith(PowerMockRunner.class)
    @PowerMockIgnore("javax.management.*")
    public class ExampleTest {
        @InjectMocks
        private ExampleServiceImpl exampleService;
        @Test
        public void testPrivateMethod() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
            Class<?>[] params = new Class<?>[]{String.class, Map.class};
            Map<String, Object> paramsMap = new HashMap<>(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.*")
    public class ExampleTest {
        @Mock
        private RedisCache redisCache;
        @Test
        public void methodNoReturnTest(){
            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(new MyException());
    // 无返回值抛异常
    Mockito.doThrow(new MyException("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);
    
    @Test
    public void testPrivateMethod2() {
        try {
            Map<String, Object> paramsMap = new HashMap<>(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 accessible
            assertEquals("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

    @Test
    public void whenAddCalledVerfied() {
        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 = new LoginContext();
    loginContext.setUser("1233");
    underTest.setCurrent(loginContext);
    
    PowerMockito.doNothing().when(executor).start(anyInt()); //给普通方法打桩
    PowerMockito.doReturn(configuration).when(HBaseConfiguration.class, "create"); //给静态方法打桩