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));
    }

}

更多使用方法请查看 文档

原文地址:https://www.cnblogs.com/markLogZhu/p/11982625.html