Java测试框架系列:Mockito 详解:第二部分:创建存根

Mockito 详解:第二部分:创建存根

零:前提条件

先明确一个词:存根(或者说是打桩),指的是对某个方法指定返回策略的操作(具体表现为两种:1指定返回值,2使用doCallRealMethod()或者thenCallRealMethod()指定当方法被调用时执行实际代码逻辑),功能就是当测试执行到此方法时直接返回我们指定的返回值(此时不会执行此方法的实际代码逻辑)或者执行此方法的实际代码逻辑并返回。比如:

我们先定义一个对象:

public class Mock {

    public String m1() {
        throw new RuntimeException();
    }
}

然后我们进行下面的操作:

Mock m = Mockito.mock(Mock.class);

//对m1()方法进行存根
Mockito.doReturn("1").when(m).m1();

基于上面的代码我们可以说:我们对m对象的m1()方法进行了存根

此时我们调用m对象的m1()方法时,可以直接得到返回值"1"而不会执行m1方法的实际代码逻辑:

Assert.assertEquals("1",m.m1());

此时断言为真。

一:创建方法存根的方式:

  • Mockito.when(foo.sum()).thenXXX(...);

    • 即对foo.sum()方法存根。
    • 存在问题
      • foo对象应该是一个mock对象。spy对象不建议使用此方式进行存根。因为当代码执行到when(foo.sum())时。foo.sum()方法会首先执行。导致sum()方法的实际代码逻辑被执行。(sum()的实际代码逻辑是否会被执行要看被spy对象的类型,当被spy对象是一个mock对象或者接口时不会执行-这些类型也没有实际代码逻辑可以执行。当被spy一个具体的对象时则实际代码逻辑会被执行)
  • Mockito.doXXX(...).when(foo).sum();

    • 即对foo.sum()方法存根。
    • 可以存根void方法。
    • foo对象可以是一个mock对象,也可以是一个spy对象。
    • 推荐使用此方式做方法存根。它可以避免上文中thenXXX()方式的问题
  • Mockito.doXXX(....).when(foo.sum());

    • 你会得到一个异常,即不应该使用这种方式!

    • org.mockito.exceptions.misusing.UnfinishedStubbingException: 
      Unfinished stubbing detected here:
      -> at c.FooTest.verifyTest(FooTest.java:23)
      
      E.g. thenReturn() may be missing.
      Examples of correct stubbing:
          when(mock.isOk()).thenReturn(true);
          when(mock.isOk()).thenThrow(exception);
          doThrow(exception).when(mock).someVoidMethod();
      Hints:
       1. missing thenReturn()
       2. you are trying to stub a final method, which is not supported
       3. you are stubbing the behaviour of another mock inside before 'thenReturn' instruction is completed
      

二:定义返回值的方式:

  • then_xxx方法 do_XXX方法 功能
    then(Answer<?> answer) doAnswer(Answer answer) 返回值使用自定义的Answer策略。
    thenAnswer(Answer<?> answer) 同上 同上。
    thenReturn(T value) doReturn(Object toBeReturned) 直接指定返回值。
    thenReturn(T value, T... values) doReturn(Object toBeReturned, Object... toBeReturnedNext) 直接指定返回值,可以定义多个返回值。第一次调用到存根方法返回第一个返回值。以此类推。超过返回值数量的调用返回参数的最后一个返回值。
    thenCallRealMethod() doCallRealMethod() 调用实际的代码逻辑。不指定返回值。
    thenThrow(Throwable... throwables) doThrow(Throwable... toBeThrown) 调用到存根方法时抛出异常。
    同上 doThrow(Class<? extends Throwable> toBeThrown) 调用到存根方法时抛出异常。可以定义多个异常。第一次调用到存根方法返回第一个异常。以此类推。超过异常数量的调用返回参数的最后一个异常。
    同上 doThrow(Class<? extends Throwable> toBeThrown, Class<? extends Throwable>... toBeThrownNext) 同上。
    无对应方法 doNothing() void方法使用的存根方式。

两种方式的示例:

public class Bar {

    public int add(int a, int b) {
        return a + b;
    }

    public void badCode() {
        throw new RuntimeException("bad bar code");
    }
}


public class Foo {

    private Bar bar;

    public int sum(int a, int b) {
        return bar.add(a, b);
    }

    public int count() {
        bar.badCode();
        return 5;
    }
}
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;

@RunWith(MockitoJUnitRunner.class)
public class MockitoTest {

    //foo 对象内部的成员变量会自动被 @Mock 注解的生成的对象注入。
    @InjectMocks
    private Foo foo;

    //bar 对象会自动的注入到 @InjectMocks 注解的对象的成员变量中去。
    @Mock
    private Bar bar;

    @Test
    public void thenTest() {
        Mockito.when(bar.add(1, 2)).then(new Answer<Integer>() {
            @Override
            public Integer answer(InvocationOnMock invocation) throws Throwable {
                return 7;
            }
        });

        int result = foo.sum(1, 2);

        Assert.assertEquals(7, result);
    }

    @Test
    public void thenAnswerTest() {
        Mockito.when(bar.add(1, 2)).thenAnswer(new Answer<Integer>() {
            @Override
            public Integer answer(InvocationOnMock invocation) throws Throwable {
                return 7;
            }
        });

        int result = foo.sum(1, 2);

        Assert.assertEquals(7, result);
    }

    @Test
    public void thenReturnTest() {
        Mockito.when(bar.add(1, 2)).thenReturn(7);

        int result = foo.sum(1, 2);

        Assert.assertEquals(7, result);

        result = foo.sum(1, 2);
        Assert.assertEquals(7, result);

        result = foo.sum(1, 2);
        Assert.assertEquals(7, result);
    }

    @Test
    public void thenReturn2Test() {
        Mockito.when(bar.add(1, 2)).thenReturn(7, 8, 9);

        int result = foo.sum(1, 2);

        Assert.assertEquals(7, result);

        result = foo.sum(1, 2);
        Assert.assertEquals(8, result);

        result = foo.sum(1, 2);
        Assert.assertEquals(9, result);

        result = foo.sum(1, 2);
        Assert.assertEquals(9, result);

        result = foo.sum(1, 2);
        Assert.assertEquals(9, result);
    }

    @Test
    public void thenCallRealMethodTest() {
        Mockito.when(bar.add(1, 2)).thenCallRealMethod();

        int result = foo.sum(1, 2);

        Assert.assertEquals(3, result);
    }

    @Test
    public void thenThrowTest() {
        Mockito.when(bar.add(1, 2)).thenThrow(new IllegalArgumentException("xxx"));

        try {
            foo.sum(1, 2);
        } catch (Exception e) {
            if (e instanceof IllegalArgumentException) {
                RuntimeException re = (RuntimeException)e;
                Assert.assertEquals("xxx", re.getMessage());
                return;
            }
        }

        Assert.fail();
    }

    @Test
    public void thenThrow2Test() {
        Mockito.when(bar.add(1, 2))
            .thenThrow(new IllegalArgumentException("xxx"), new IllegalArgumentException("yyy"));

        Exception e1 = null;

        try {
            foo.sum(1, 2);
        } catch (Exception e) {
            e1 = e;
        }
        if (e1 instanceof IllegalArgumentException) {
            Assert.assertEquals("xxx", e1.getMessage());
            e1 = null;
        } else {
            Assert.fail();
        }

        try {
            foo.sum(1, 2);
        } catch (Exception e) {
            e1 = e;
        }
        if (e1 instanceof IllegalArgumentException) {
            Assert.assertEquals("yyy", e1.getMessage());
            e1 = null;
        } else {
            Assert.fail();
        }

        try {
            foo.sum(1, 2);
        } catch (Exception 
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值