• Mock


    一、Mock

    1.1 什么是 Mock

    mock 是在测试过程中,对于一些不容易构造/获取的对象,创建一个 mock 对象来模拟对象的行为。
    

    1.2 什么时候使用

    * 单元测试时,使用外部资源或第三方库代码
    * 并行开发时,另一方还没有开发完毕    
    

    1.3 Mock 分类

    • Mock 对象
    主要适用于单元测试,写入一些预期的值,通过它进行自己想要的测试。
    
    • Mock Server
    主要适用于接口和性能测试,构造一个假的服务返回预期的结果,进行测试。
    

    1.4 技术选型

    Java 阵营 中主要的 Mock 测试工具有 **Mockito ,JMock,MockCreator,Mockrunner,EasyMock,MockMaker,PowerMock ** 等

    我们这里重点讲解 **Mockito 和 PowerMock ** 。

    二、Mockito 实践

    2.1 引入依赖

    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-all</artifactId>
        <version>1.10.19</version>
        <scope>test</scope>
    </dependency>
    

    2.2 验证行为

    @Test
    public void testList(){
        // 创建模拟对象
        List<String> mockList = mock(List.class);
    
        // 模拟对象执行操作
        mockList.add("one");
        mockList.clear();
    
        // 验证
        verify(mockList).add("one");
        verify(mockList).clear();
    }
    

    执行程序,发现测试通过,证明 Mockito 可以验证行为。

    2.3 验证行为调用次数

    @Test
    public void testListBehaviorExecutionFrequency() {
        // 创建模拟对象
        List<String> mockList = mock(List.class);
    
        // 模拟对象执行操作
        mockList.add("1");
        mockList.add("2");
        mockList.add("2");
        mockList.add("3");
        mockList.add("3");
        mockList.add("3");
        mockList.add("4");
        mockList.add("4");
        mockList.add("4");
        mockList.add("4");
        mockList.clear();
    
        // 验证
        verify(mockList).add("1");
        // 验证执行次数
        verify(mockList, times(1)).add("1");
        verify(mockList, times(2)).add("2");
        verify(mockList, times(3)).add("3");
        verify(mockList, times(4)).add("4");
        verify(mockList, times(1)).clear();
    
        // 验证从来没有执行的方法
        verify(mockList, never()).add("0");
    
        // 至少执行过一次
        verify(mockList, atLeastOnce()).add("4");
        // 最小执行过2次
        verify(mockList, atLeast(2)).add("4");
        // 最多执行过5次
        verify(mockList, atMost(5)).add("4");
    }
    

    2.4 验证行为执行的顺序

    @Test
    public void testListBehaviorExecutionOrderBySingle(){
        // 创建模拟对象
        List<String> mockList = mock(List.class);
    
        // 模拟对象执行操作
        mockList.add("1");
        mockList.add("2");
        mockList.add("3");
    
        InOrder inOrder = inOrder(mockList);
        // 验证先执行add 1 ,再执行添加 3,可以发现跳过了添加 2 的行为,说明可以只关注感兴趣的交互即可
        inOrder.verify(mockList).add("1");
        inOrder.verify(mockList).add("3");
    }
    
    @Test
    public void testListBehaviorExecutionOrderByMultiple() {
        // 创建模拟对象
        List<String> firsMockList = mock(List.class);
        List<String> secondMockList = mock(List.class);
    
        // 模拟对象执行操作
        firsMockList.add("firsMockList add ");
        secondMockList.add("secondMockList add ");
    
        InOrder inOrder = inOrder(firsMockList,secondMockList);
         // 验证执行顺序
        inOrder.verify(firsMockList).add("firsMockList add ");
        inOrder.verify(secondMockList).add("secondMockList add ");
    }
    

    2.5 验证从未调用过的对象

    @Test
    public void testNoOperating() {
        List<String> notOperatingList = mock(List.class);
        verifyNoInteractions(notOperatingList);
    }
    

    2.6 查找冗余调用

    @Test
    public void testNoMoreInteractions(){
        List<String> mockList = mock(List.class);
    
        mockList.add("one");
        mockList.add("two");
    
        verify(mockList).add("one");
    
        //将抛出异常,因为 `mockList.add("two");` 是额外的调用
        verifyNoMoreInteractions(mockList);
    }
    

    2.7 模拟存根

    @Test
    public void testStubbing(){
        // 创建模拟对象
        List mockList = mock(List.class);
    
        // 启用存根方法,当什么时候执行什么操作
        when(mockList.get(0)).thenReturn("1");
        when(mockList.get(3)).thenReturn("3");
    
        Assert.assertEquals("1",mockList.get(0));
        Assert.assertEquals("3",mockList.get(3));
        Assert.assertEquals(null,mockList.get(99));
    }
    

    说明:

    * 默认情况下,对于所有返回值的方法,模拟将酌情返回null,原始/原始包装器值或空集合。例如,对于int / Integer为0,对于boolean / Boolean为false。
    * 存根可以被覆盖:例如,通用存根可以进入夹具设置,但是测试方法可以覆盖它。请注意,过多的存根是潜在的代码异味,表明存在过多的存根
    * 一旦存根,该方法将始终返回存根值,而不管其被调用了多少次。
    * 最后一次存根更为重要-当您多次对具有相同参数的相同方法进行存根时。换句话说:存根的顺序很重要,但很少有意义,例如,存根完全相同的方法调用时或有时使用参数匹配器时,等等。
    

    2.8 模拟异常抛出

    @Test
    public void testThrowException(){
        // 创建模拟对象
        List mockList = mock(List.class);
        // 指定什么操作将抛出异常
        doThrow(new RuntimeException()).when(mockList).clear();
        // 该方法将抛出异常
        mockList.clear();
    }
    

    2.9 模拟创建的简写- @Mock注释

    @RunWith(MockitoJUnitRunner.class)
    public class MockTest{
    
        @Mock
        private List list;
        @Test
        public void testMockAnnotation(){
            list.add("one");
            verify(list).add("one");
        }
    }   
    

    需要配合 @RunWith(MockitoJUnitRunner.class) 注解使用。

    2.10 参数匹配器

    实现类:

    @Service
    public class UserServiceImpl implements UserService {
    
        private final SysUserMapper sysUserMapper;
    
        public UserServiceImpl(SysUserMapper sysUserMapper) {
            this.sysUserMapper = sysUserMapper;
        }
    
        @Override
        public int count(String account) {
            SysUserExample example = new SysUserExample();
            example.createCriteria().andAccountEqualTo(account);
            List<SysUser> users = sysUserMapper.selectByExample(example);
            return users.size();
        }
    
    }
    

    测试类:

    public class UserServiceTest {
        private UserService userService;
        private SysUserMapper sysUserMapper;
    
        @Before
        public void setUp() {
            sysUserMapper = mock(SysUserMapper.class);
            userService = new UserServiceImpl(sysUserMapper);
        }
        
        @Test
        public void testMock() {
            // 准备数据
            String account1 = "admin";       
            List<SysUser> userList = new ArrayList<>();
            userList.add(getOneUser(account1));
            // 模拟行为
            when(sysUserMapper.selectByExample(any(SysUserExample.class)))
                    .thenReturn(userList);
            // 执行方法
            int result = userService.count(account1);
            // 验证结果
            assertThat(result, is(1));
        }
    }    
    

    其他类型参数:

    anyInt();
    anyString();
    anyX();
    

    更多使用方法请查看 文档

    三、PowerMock 实践

    PowerMock 主要用于静态和私有方法的模拟。

    3.1 引入依赖

    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-module-junit4</artifactId>
        <version>2.0.2</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-api-mockito2</artifactId>
        <version>2.0.2</version>
        <scope>test</scope>
    </dependency>
    

    3.2 使用说明

    相关注解:所有测试类均须加上以下注解

    // 表明用 PowerMockerRunner来运行测试用例,否则无法使用PowerMock
    @RunWith(PowerMockRunner.class)
    // 所有需要测试的类,列在此处,以逗号分隔
    @PrepareForTest({UserController.class, FileHelper.class})
    

    3.3 模拟静态方法调用

    public class PrintUtils {
    
        public static String getPirntMessage() {
            return "123";
        }
    }
    
    @RunWith(PowerMockRunner.class)
    @PrepareForTest(PrintUtils.class)
    public class PrintUtilsTest {
    
    
        @Test
        public void shouldReturnExpectedString() {
            String expectedString = "mockString";
            
            PowerMockito.mockStatic(PrintUtils.class);
            PowerMockito.when(PrintUtils.getPirntMessage()).thenReturn(expectedString);
    
            String result =  PrintUtils.getPirntMessage();
            assertThat(result,is(expectedString));
        }
    
    }
    

    更多使用方法请查看 文档

  • 相关阅读:
    CSS 定位position
    new Date(time).getTime()在ios返回NaN
    babel.js 文件 browser.min.js
    export 和 export default 的区别
    第一个webpack例子demo1
    Kafka源码解析与实战
    Redis实战
    RabbitMQ实战
    Spring源码理解
    Java并发编程实践
  • 原文地址:https://www.cnblogs.com/markLogZhu/p/11982625.html
Copyright © 2020-2023  润新知