Mock 测试

大厂的 App 可能都有一个特点,那就是依赖错综复杂,神龙见首不见尾。至今仍感叹公司的 App 还可以打出包并且能欢快的跑起来!【惊叹脸】模块化是模块化了,但这样就给我们写测试用例造成了困难:数据库连不上?服务起不来?依赖找不到?

莫担心,祭出我们的大杀器 Mock !

什么是 Mock测试?

Mock测试就是在测试过程对于那些不易构造或不易获取的对象,用一个虚假的对象来代替。目前 java 中比较流行的 Mock测试框架有 Mockito, PowerMock, easyMock, Mockito 便是我们这次要学习的内容。

Mockito 依赖

使用 Mockito 是很简单的,直接在 build.gradle 中添加

testCompile 'org.mockito:mockito-core:2.10.0'复制代码

更详细的内容可以访问 Mockito官网

小试牛刀

环境配置好了,是时候编造点需求啦!

public class StaffManage {

    private StaffDao mStaffDao;

    public StaffManage(StaffDao staffDao){
        this.mStaffDao = staffDao;
    }

    public int getStaffCount(){
        return mStaffDao.getStaffCount();
    }

    public void addStaff(Staff staff){
        mStaffDao.addStaff(staff);
    }
}复制代码

这是我们的员工管理类,该类负责和数据库打交道。满心欢喜的打算跑一跑时,却返现 StaffDao 还没有就绪!

class StaffDao {
    public int getStaffCount() {
        throw new UnsupportedOperationException();
    }

    public void addStaff(Staff staff) {
        throw new UnsupportedOperationException();
    }
}复制代码

这就略显尴尬了,如果只有普通的单元测试肯定是通不过的。那么如果 StaffDao 要拖很久才能开发完成,在这种情况下我们怎么对我们的代码进行验证呢?

public class StaffManageTest {

    private StaffManage mStaffManage;
    //我们通过使用 mock 方法虚拟一个 StaffDao 对象
    private StaffDao staffDao = Mockito.mock(StaffDao.class);
    @Before
    public void setUp(){
        this.mStaffManage = new StaffManage(staffDao);
    }

    @Test
    public void getStaffCount() throws Exception {
        //当虚拟对象调用 getStaffCount() 方法时就返回 200
        Mockito.when(staffDao.getStaffCount()).thenReturn(200);
        //我们可以通过返回值验证函数是否正确
        Assert.assertEquals(200, mStaffManage.getStaffCount());
    }

    @Test
    public void addStaff() throws Exception {
        Staff staff = new Staff();
        mStaffManage.addStaff(staff);
        //无返回值时,可以通过断言方法的调用来验证
        Mockito.verify(staffDao).addStaff(staff);
    }

}复制代码

以上我们就在 StaffDao 未开发完成的情况下,完成了对 StaffManage 的功能验证。
当然 Mockito 的功能远不止如此,接下来一起看看还有那些功能?

Mockito 功能集合

在之后我们要有 “桩” 的概念,在测试的时候我们是不能改变现有的代码的,那么我们通过什么判断代码时候正确执行呢?我们利用 Mock 的对象代替真实的对象,插入到代码中,观察虚拟对象的状态达到检测的目的,也就是 “插桩”。

验证一些行为

对于一些没有返回值的方法,我们不能直接通过验证返回值来判断方法的正确与否。此时验证一些关键方法的调用便成了我们唯一的选择。

// mock 虚拟对象
List mockList = mock(List.class);
// 使用虚拟对象
mockList.add("one");
mockList.clear();
// 验证方法的调用
verify(mockList).add("one");
verify(mockList).clear();复制代码

怎么构造返回值

由于 Mock对象 并不会真的执行方法中的代码,所以如果未指定返回值的话会返回默认值,比如 int 时返回 0,boolean 时返回 false, 非基本数据时会返回 null.

//不止接口,任何类都可以通过 mock 构造虚拟对象
LinkedList linkedList = mock(LinkedList.class);
when(linkedList.get(0)).thenReturn("one");
when(linkedList.get(1)).thenReturn(new IndexOutOfBoundsException());

System.out.println(linkedList.get(0));  // one
System.out.println(linkedList.get(1));  // java.lang.IndexOutOfBoundsException
System.out.println(linkedList.get(2));  // null复制代码

参数匹配

有时候我们并不关心参数的细节,只需参数满足一定的条件即可,这个时候我们可以用参数适配器来做更灵活的配置。

linkedList.add("element");
// anyInt() 任何整数我们都返回 element 
when(linkedList.get(anyInt())).thenReturn("element");

System.out.print(linkedList.get(10));
// 同样可以用在断言处,标识是否该方法被调用了,并且传入 int 类型
verify(linkedList).get(anyInt());
// 通过 ArgumentMatcher 我们可以拿到该模拟方法接受的参数
verify(linkedList).add(argThat((ArgumentMatcher<String>) argument -> argument.length() == 7));复制代码

验证被调用的确切的次数

在一些黑盒方法中,我们可能需要方法确切的被调用的次数,或者确定不被调用。Mockito 为我们提供了很好的支持。

mockedList.add("once");

mockedList.add("twice");
mockedList.add("twice");

mockedList.add("three times");
mockedList.add("three times");
mockedList.add("three times");

// 默认情况 跟 times(1) 是一样的 
verify(mockedList).add("once");
verify(mockedList, times(1)).add("once");

// 可以指定确切的次数
verify(mockedList, times(2)).add("twice");
verify(mockedList, times(3)).add("three times");

// never() 相当于 times(0)
verify(mockedList, never()).add("never happened");

// 还有 atLeast() 和 atMost() 提供更灵活的验证
verify(mockedList, atLeastOnce()).add("three times");
verify(mockedList, atLeast(2)).add("five times");
verify(mockedList, atMost(5)).add("three times");复制代码

在调用方法时抛出异常

doThrow(new RuntimeException()).when(mockedList).clear();

//following throws RuntimeException:
mockedList.clear();复制代码

验证调用的顺序

有时候单单验证方法的调用是不够的,我们还需要确定方法是不是按照正确的顺序调用。

 List singleMock = mock(List.class);

 singleMock.add("was added first");
 singleMock.add("was added second");

 // 每个 mock 对象对应一个 inOrder
 InOrder inOrder = inOrder(singleMock);

 // 验证调用顺序
 inOrder.verify(singleMock).add("was added first");
 inOrder.verify(singleMock).add("was added second");复制代码

确定有些模拟对象没有交互过

用于验证一些 Mock对象 在之前的交互过程中,没有被交互过。

//using mocks - only mockOne is interacted
mockOne.add("one");

//ordinary verification
verify(mockOne).add("one");

//verify that method was never called on a mock
verify(mockOne, never()).add("two");

//verify that other mocks were not interacted
verifyZeroInteractions(mockTwo, mockThree);复制代码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值