• 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.*")
    @PrepareForTest mock静态类 @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)
    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, "");
    }
    
  • 相关阅读:
    《数据结构
    《数据结构
    《数据结构
    《算法
    《linux 进程管理》- ps/top/kill/killall/nice
    《linux 字符处理》- grep/sort/uniq/tr/paste/sed/awk
    《linux 文件目录》- touch/rm/mv/cat/head/tail/cp/mkdir/chmod/chown/find/locate/which/whereis/tar
    MySQL优化必须调整的10项配置
    PV-UV
    linux+nginx+mysql+php高性能服务器搭建
  • 原文地址:https://www.cnblogs.com/jockming/p/16058960.html
Copyright © 2020-2023  润新知