Java测试-mockito学习

1 Mockito

mockito是Java中用于单元测试的模拟框架,Mockito库支持模拟创建、验证和存根。

通常和Junit框架组合完成对项目的单元测试。

2 各种模拟使用

2.1 mock接口并验证

测试demo-1:

//1--mock一些接口, 使用verify验证某些行为是否发生仅过一次
    @Test
    void mockListTest() {
        List listMock = mock(List.class);
        listMock.add("one");
        listMock.get(1);
        listMock.clear();
        verify(listMock).add("one");
        verify(listMock).clear();
        verify(listMock).get(1);
    }

mock接口,并可verify行为是否发生过和已经发生次数。

2.2 mock具体类,并进行stub

测试demo-2:

//2--mock一些具体的类,并进行stub
    @Test
    void mockConcreteList() {
        LinkedList mockLinkedList = mock(LinkedList.class);
        when(mockLinkedList.get(0)).thenReturn("first");
        when(mockLinkedList.get(1)).thenThrow(new RuntimeException());
        System.out.println(mockLinkedList.get(0));
//        System.out.println(mockLinkedList.get(1));
        System.out.println(mockLinkedList.get(999));
    }

mock具体类,stub具体的行为返回值。

2.3 参数匹配器-Argument matchers

测试demo-3:

@Test
    void mockArgumentMatcherTest() {
        LinkedList mockLinkedList = mock(LinkedList.class);
        when(mockLinkedList.get(anyInt())).thenReturn("element");
        System.out.println(mockLinkedList.get(Integer.MAX_VALUE));
        //可以传入lambda表达式
        when(mockLinkedList.contains(argThat((str) -> {
            String strNew = (String) str;
            return strNew.length() > 5;
        }))).thenReturn(true);
        System.out.println(mockLinkedList.contains("cece"));
        System.out.println(mockLinkedList.contains("cecece"));
        verify(mockLinkedList).get(anyInt());
    }

运行结果:
在这里插入图片描述
可以使用内建的anyInt……系列或者自定义argThat,来完成stub需求,支持lambda表达式

2.4 验证实际调用的次数

测试demo-4:

@Test
    void verifyExactNumberTest() {
        LinkedList mockLinkedList = mock(LinkedList.class);
        mockLinkedList.add("one");

        mockLinkedList.add("two");
        mockLinkedList.add("two");

        mockLinkedList.add("three");
        mockLinkedList.add("three");
        mockLinkedList.add("three");

        //默认一次
        verify(mockLinkedList).add("one");
        verify(mockLinkedList, times(1)).add("one");

        //验证两次、三次
        verify(mockLinkedList, times(2)).add("two");
        verify(mockLinkedList, times(3)).add("three");

        //验证从未添加的元素
        verify(mockLinkedList, never()).add("never add");

        //验证atLeast、atMost
        verify(mockLinkedList, atLeast(1)).add("three");
        verify(mockLinkedList, atMost(1)).add("one");

    }

可以使用支持的times、never、atLeast和atMost进行调用次数验证。

2.5 stub 空返回值方法

测试demo-5:

@Test
    void stubVoidTest() {
        LinkedList mockLinkedList = mock(LinkedList.class);
        doThrow(new RuntimeException()).when(mockLinkedList).clear();
        //调用clear方法,返回异常
        mockLinkedList.clear();
    }

执行结果:
在这里插入图片描述

mock返回值为void方法。

2.6 按顺序验证行为

测试demo-6:

@Test
    void verifyInOrderTest() {
        List singleList = mock(List.class);
        singleList.add("was add first");
        singleList.add("was add second");

        //单一mock验证顺序
        InOrder singleInOrder = inOrder(singleList);
        singleInOrder.verify(singleList).add("was add first");
        singleInOrder.verify(singleList).add("was add second");

        List firstMock = mock(List.class);
        List secondMock = mock(List.class);

        firstMock.add("one");
        firstMock.add("two");

        secondMock.add("one");
        secondMock.add("two");

        InOrder order = inOrder(firstMock, secondMock);
        //多个mock验证添加顺序
        order.verify(firstMock).add("two");
        order.verify(secondMock).add("one");
        order.verify(secondMock).add("two");
    }

通过InOrder进行行为顺序验证,支持多个mock对象的顺序验证。

2.7 冗余的mock对象验证

测试demo-7:

@Test
    void redundantInvocationTest() {
        List mockList = mock(List.class);
        mockList.add("one");
        mockList.add("two");
        //这个没有验证
        mockList.add("three");

        verify(mockList).add("two");
        verify(mockList).add("one");

        verifyNoMoreInteractions(mockList);
    }

执行结果:
在这里插入图片描述

verifyNoMoreInteractions支持验证多余的交互行为(交互了但没有verify)。

2.8 迭代器风格的stub

测试demo-8:

@Test
    void stubIteratorTest() {
        List mockList = mock(List.class);

        when(mockList.get(1)).thenReturn("foo", "test");
        //按顺序返回
        System.out.println(mockList.get(1));
        System.out.println(mockList.get(1));
        //之后都是最后一个迭代test获胜
        System.out.println(mockList.get(1));
    }

执行结果:
在这里插入图片描述

可以返回多个值,每次调用都会向后迭代,超过长度会重复最后一个stub的结果。

2.9 支持回调的stub

测试demo-9:

@Test
    void stubWithCallbackTest() {
        List<String> mockList = mock(List.class);
        //使用thenAnswer传入回调
        when(mockList.get(anyInt())).thenAnswer(invocation -> {
            Object[] arguments = invocation.getArguments();
            Object mock = invocation.getMock();
            return "called with arguments: " + Arrays.toString(arguments);
        });
        System.out.println(mockList.get(111));
    }

运行结果:
在这里插入图片描述
支持通过thenAnswer传入lambda表达式,自定义回调函数处理返回结果。

2. 10 spy真实对象

测试demo-10:

@Test
    void spyRealObjectTest() {
        LinkedList<String> linkedList = new LinkedList<>();
        LinkedList<String> spy = spy(linkedList);

        spy.add("one");
        spy.add("two");

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

        //进行stub
        when(spy.size()).thenReturn(100);
        System.out.println(spy.size());

        verify(spy).add("one");


        List list = new LinkedList();
        List spy1 = spy(list);

    }

运行结果:
在这里插入图片描述
spy真实对象,将会调用对象的真实方法,同时可以支持stub对象行为,但是有局限性,比如下面情况:

//Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty)
        when(spy1.get(0)).thenReturn("foo");

        //You have to use doReturn() for stubbing
        doReturn("foo").when(spy).get(0);

运行结果:
在这里插入图片描述
因为使用when,spy.get(0)将被调用,而此时还没有元素,会抛出异常。而doReturn没有调用方法,只是设置了返回值。

2-11 部分真实mock

测试demo-11:

@Test
    void realPartMockTest() {
        CalculatorMapper mock = mock(CalculatorMapper.class);
        //调用selectFirst调用真实方法
        when(mock.selectFirst(anyInt())).thenCallRealMethod();

        Assertions.assertEquals(101, mock.selectFirst(1));
    }

thenCallRealMethod支持调用真实对象方法。

2-12 重置mock

测试demo-12:

@Test
    void resetMockTest() {
        List mock = mock(List.class);
        when(mock.size()).thenReturn(10);
        mock.add(1);

        //reset所有发生的调用和行为
        reset(mock);
        verifyNoMoreInteractions(mock);
    }

一般我们测试都是针对单独的一个功能,没有必要进行重置。

2-13 @Mock和@InjectMocks

测试demo-13:

@ExtendWith(MockitoExtension.class)
public class CalculatorManagerTest {

    //注入mock,会对注入的属性根据测试类中定义的mock类进行注入。
    @InjectMocks
    private CalculatorService calculatorService;

    //mock其它类,需要对方法返回值进行mock设置
    @Mock private CalculatorMapper calculatorMapper;

    @BeforeEach
    void setUp() {
    }

    @Test
    public void calculatorManagerTest() {
        when(calculatorMapper.selectFirst(anyInt())).thenReturn(100);
        when(calculatorMapper.selectSecond(anyInt())).thenReturn(100);
        Assertions.assertEquals(200, calculatorService.add(1, 1));
    }
}

可以使用注解@Mock更简单的mock一个对象,如果对象中有注入对象,可以使用@InjectMocks进行mock对象注入。

2-14 行为驱动开发模式BDD

BDD一般定义为下:

  • 给定given:场景开头的初始上下文,一个或多个子句.
  • 时间when:触发场景的事件.
  • 然后then:在一个或多个条款中的预期结果。

测试demo-14:

@Test
    void bddMockTest() {
        List mockList = mock(List.class);

        //given,列表获取常见
        given(mockList.get(anyInt())).willReturn(100, 200, 300);

        //when,第一次获取
        mockList.get(0);

        //then,判断获取第二次结果
        Assertions.assertEquals(mockList.get(0), 200);

        //verify行为的bdd模式
        then(mockList).should(times(2)).get(0);

    }

mockito工作组建议使用的编写测试模式。

库中已经封装了对于的BDD调用方式。

参考文献

[1] Mockito文档.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LamaxiyaFc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值