关于 Mockito BDDMockito

前言

向很多开源社区提交代码是需要做完整的 单元测试 的,其中 mock 目标实例、给 mock 对象打桩 等操作可以极大提高测试代码的效率和可读性

本章节基于 Mockito BDDMockitoAPI DEMO 熟悉一些基本操作,Spring Test 包含对应依赖

Mockito BDDMockito

其中 BDD 代表 behavior driven deployment,我理解就是让测试代码语义更接近现实行为,比如

  • given 就是 给定 ... 条件下,可以类比为 打桩(当然也可以不打)
  • when 就是 发生 ... 行为,比如 mock 实例的方法调用(当然也可以不发生)
  • then,就是 则 ...打桩mock 实例执行目标方法后的校验
语义化的东西强行解释总感觉生硬

mock

	@Test
    public void mockDemo() {

        List mock = mock(List.class);

        mock.add("test");
        mock.add("test1");
        mock.add("test1");
        mock.get(0);

        // verify:是否调用目标方法
        verify(mock).add("test");
        // BDD style
        then(mock).should().add("test");

		// 目标方法调用一次
        verify(mock, times(1)).add("test");
        then(mock).should(times(1)).add("test");

		// 目标方法至少调用 2 次
        verify(mock, atLeast(2)).add("test1");
        then(mock).should(atLeast(2)).add("test1");

		// 目标方法最多调用 2 次
        verify(mock, atMost(2)).add("test1");
        then(mock).should(atMost(2)).add("test1");

		// 目标方法未调用
        verify(mock, never()).add("test2");
        then(mock).should(never()).add("test2");

        // error
        verify(mock).add("test2");
        verify(mock).get(1);
    }
  • mock 方法创建 mock 对象,即一个临时虚拟对象,测试基于该示例进行
  • verifyMockito 下的 API,类似于断言,比如 verify(mock).add("test") 就是检验该方法是否调用
  • thenBDDMockitoAPI,类比于 verify 更加语义化

stub

	@Test
    public void stubDemo() {

        ArrayList mock = mock(ArrayList.class);

        // 打桩
        when(mock.get(0)).thenReturn("a");
        // BDD-style
        given(mock.get(0)).willReturn("a");

        when(mock.get(1)).thenThrow(new RuntimeException("test"));
        // given(mock.get(1)).willThrow(new RuntimeException("test"));

        doThrow(new RuntimeException("void")).when(mock).clear();
        // willThrow(new RuntimeException("void")).given(mock).clear();

        System.out.println(mock.get(0));
        try {
            mock.get(1);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        System.out.println(mock.get(2));
    }
  • 这是一段 stub 代码,即打桩操作,主要应付方法逻辑复杂且非测试目标的场景
  • 对应的 Mockito APIwhen ... then ...
  • 对应的 BDDMockitothen ... will ...

matcher

	@Test
    public void matcherDemo() {

        ArrayList mock = mock(ArrayList.class);

        // 匹配任意 int
        when(mock.get(anyInt())).thenReturn("meta");
        given(mock.get(anyInt())).willReturn("meta");

        // 所有的方法都要使用 matcher
        when(mock.subList(anyInt(), eq(1))).thenReturn(new ArrayList() {{
            add("meta2");
        }});
        given(mock.subList(anyInt(), eq(1))).willReturn(new ArrayList() {{
            add("meta2");
        }});

        System.out.println(mock.get(12));
        System.out.println(mock.subList(3, 1));

        // then 阶段也可以使用
        verify(mock).get(anyInt());
        then(mock).should().get(anyInt());
    }
  • 可以对参数进行匹配,比如 anyInt() 匹配任意 Integer
  • 这个针对 Mockito BDDMockito 都一样
  • 自然语义下的 then 阶段也可以使用 matcher 来匹配
我指的自然语义就是 given ... when ... then ...

inorder

	@Test
    public void orderDemo() {

        ArrayList mock = mock(ArrayList.class);
        LinkedList mock2 = mock(LinkedList.class);

        mock.add(1);
        mock.add(2);
        mock2.add(1);
        mock2.add(2);

		// 可以有多个实例
        InOrder inOrder = inOrder(mock, mock2);

        inOrder.verify(mock).add(1);
        inOrder.verify(mock).add(2);
        
        // correct,可以跳过
        // inOrder.verify(mock2).add(2);
        inOrder.verify(mock2).add(1);
        // BDD-style
        then(mock2).should(inOrder).add(2);

        // error
        inOrder.verify(mock).add(1);
    }
  • 这段代码可以测试 mock 实例的方法执行顺序是否符合预期
  • 可以多个实例、多个方法结合使用,可看 demo 理解
  • 顺序前后符合即可,可以不严格,即 1 2 3 4 可以是 1 2 41 3 4 但不可以 2 1 3 4

consecutive

	@Test
    public void consecutiveDemo() {
        ArrayList mock = mock(ArrayList.class);

        when(mock.get(anyInt()))
                .thenReturn(0, 1, 2);

        // 0
        System.out.println(mock.get(0));
        // 1
        System.out.println(mock.get(1));
        // 2
        System.out.println(mock.get(2));
        // 2
        System.out.println(mock.get(3));

        // 清空
        reset(mock);
        System.out.println(mock.get(0));
    }
  • 打桩 的结果可以指定多个,会依此返回
  • 如果没有其他结果,则保持最后一个
  • reset 可情况 mock 实例在自然语义 givenwhen 阶段下的所有行为

answer

	@Test
    public void answerDemo() {
        ArrayList mock = mock(ArrayList.class);

        when(mock.get(anyInt()))
                .then(invocation -> {
                    Object argument = invocation.getArgument(0);
                    return argument + "r";
                });
        // BDD-style
        given(mock.get(anyInt()))
                .will(invocationOnMock -> {
                    Object argument = invocationOnMock.getArgument(0);
                    return argument + "r";
                });

        System.out.println(mock.get(0));
        System.out.println(mock.get(1));
    }
  • 打桩 结果指定更加灵活的 Answer
  • 示例中是基于 lambda 风格的实现

spy

	@Test
    public void spyDemo() {
        List list = new LinkedList();
        List spy = spy(list);

        spy.add(1);

        when(spy.size()).thenReturn(10);
        // BDD-style
        given(spy.size()).willReturn(10);

        System.out.println(spy.get(0));
        System.out.println(spy.size());

        // spy 只是一个 copy,所以 list 上的操作不影响 spy
        list.add(2);
        spy.forEach(System.out::println);
    }
  • spy 方法可返回一个 spy 实例,该实例除了 打桩 行为外的调用都与原实例相同,比如示例中 spy.add(1) 方法就是正常的 ArrayList::add,而 spy.size() 因为被 stub 只返回 10
  • 值得注意的是,原对象上的操作并不影响 spy 示例,比如示例中 list.add(2) 并不意味着 spy.get(1) == 2

oneLinerStubs

	@Test
    public void oneLinerStubsDemo() {
        HelloService helloService = when(mock(HelloService.class).hello())
                .thenReturn("hello world")
                .getMock();
        // BDD-style
        HelloService helloService2 = given(mock(HelloService.class).hello())
                .willReturn("hello world 2")
                .getMock();
        System.out.println(helloService.hello());
        System.out.println(helloService2.hello());
    }
  • 一行式 demo
  • getMock 方法返回 mock 实例

总结

本文介绍一些基于 Mockito BDDMockitoDEMO,旨在写出实用又好看的 单元测试

完整示例 demo

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值