Mockito


前言

学习B站汪文君Mockito实战视频的笔记


1 mock的三种方式

1.1 MockByRunner

第一种mock方式,这种方式的mock的对象可以是测试函数的局部变量

package com.lanshang.mockito.lesson3;

import com.lanshang.mockito.quickstart.AccountDao;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;

//第一种mock方式,这种方式的mock的对象可以是测试函数的局部变量
@RunWith(MockitoJUnitRunner.class)
public class MokeByRunner {

    @Test
    public void test(){
        AccountDao dao = Mockito.mock(AccountDao.class);
    }
}

1.2 MockByAnnotation

第二种mock方式,注释的方式,这种方式mock的对象不可以是测试函数的局部变量,所以这里把需要mock的对象提到测试类的成员变量

package com.lanshang.mockito.lesson3;

import com.lanshang.mockito.quickstart.AccountDao;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

//第二种mock方式,注释的方式,这种方式mock的对象不可以是测试函数的局部变量,所以这里把需要mock的对象提到测试类的成员变量
public class MockByAnnotation {
    @Mock
    AccountDao accountDao;

    @Before
    public void init(){
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void test(){
        accountDao.findAccount("x","x");
    }
}

1.3 MockByRule

注意:这种mock方法在高版本可用,在1.9.0的mockito-all不支持

package com.lanshang.mockito.lesson3;

import com.lanshang.mockito.quickstart.AccountDao;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
//注意:这种mock方法在高版本可用,在1.9.0的mockito-all不支持
public class MockByRule {
    @Mock
    AccountDao accountDao;

    @Rule
    public MockitoRule mockitorule = MockitoJUnit.rule();

    @Test
    public void test(){
        accountDao.findAccount("x","x");
    }
}

2 深度mock

注意:深度Moke并不能确保每次都生效.
使用方法:在需要mock的对象注释上头这么写: @Mock(answer = Answers.RETURNS_DEEP_STUBS)

package com.lanshang.mockito.lesson3;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

public class DeepMode {
    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
    A a;

    @Before
    public void init(){
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void test(){
        a.getB().a();
    }
}

class A{
    public B getB(){
        throw new RuntimeException();
    }
}

class B{
    public void a(){
        throw new RuntimeException();
    }
}

3 Stub

3.1 有返回值的函数的测试方法

注意:list已经mock

    @Test
    public void testStubbing1(){
        //测试有返回值的函数的正常情况
        when(list.get(0)).thenReturn("0");
        //上述写法与下面的写法等同
        //doReturn("0").when(list).get(0);
        assertThat(list.get(0),equalTo("0"));

        //测试有返回值的函数的异常情况
        when(list.get(1)).thenThrow(new RuntimeException());
        try {
            list.get(1);
            fail();
        } catch (Exception e) {
            assertThat(e,instanceOf(RuntimeException.class));
        }

    }

3.2 无返回值的函数的测试方法

注意:list已经mock

//测试没有返回值的函数
    @Test
    public void testStubbing2(){

        //测试无返回值的函数的正常情况
        doNothing().when(list).clear();
        list.clear();
        verify(list,times(1)).clear();

        //测试无返回值的函数的异常情况
        doThrow(new RuntimeException()).when(list).clear();
        try {
            list.clear();
            fail();
        } catch (Exception e) {
            assertThat(e,instanceOf(RuntimeException.class));
        }

    }

3.3 遍历stub

    @Test
    public void testIterateStubbing(){
        //指定list.get(0)第一次调用结果为1,第二次调用结果为2,第三次调用结果为3
        when(list.get(0)).thenReturn(1,2,3);
        //下述写法等价于when(list.get(0)).thenReturn(1,2,3);
        //when(list.get(0)).thenReturn(1).thenReturn(2).thenReturn(3);
        assertThat(list.get(0),equalTo(1));
        assertThat(list.get(0),equalTo(2));
        assertThat(list.get(0),equalTo(3));
        //第三次后调用结果重复第三次调用的值
        assertThat(list.get(0),equalTo(3));
        assertThat(list.get(0),equalTo(3));
    }

3.4 when().thenAnswer()用法

when().thenAnswer()用于简化when里方法的每个参数要有同样的逻辑处理。
比如返回list.get()入参的10倍:
when(list.get(0)).thenReturn(0);
when(list.get(1)).thenReturn(10);
when(list.get(23)).thenReturn(230);
……
就可以写成以下方式:

    @Test
    public void testStubbingWithAnswer(){
        when(list.get(anyInt())).thenAnswer(new Answer<Object>() {
            @Override
            public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
                //invocationOnMock.getArguments()获取list.get()函数所有的入参,
                //因为list.get()函数只有一个入参,所以用invocationOnMock.getArguments()[0]取出该入参的值
                Integer index = (Integer)invocationOnMock.getArguments()[0];
                return index*10;
            }
        });
        assertThat(list.get(0),equalTo(0));
        assertThat(list.get(1),equalTo(10));
        assertThat(list.get(23),equalTo(230));
    }

3.5 when().thenCallRealMethod()用法

如果想mock出的对象真正执行其方法,可以用when().thenCallRealMethod()

class Service{
    public Integer light(){
        System.out.println("light()方法执行!");
        return 999;
    }

    public String weight(){
        System.out.println("weight()方法执行!");
        return "weight……";
    }
}
    @Test
    public void testRealCall(){
        Service service = mock(Service.class);
        //mock出的对象调用其方法不会真的执行该方法,而是返回其返回类型的默认值
        //service.light()的返回类型是Integer,所以输出0
        System.out.println(service.light());
        //service.weight()的返回类型是String,所以输出null
        System.out.println(service.weight());

        //如果想mock出的对象真正执行其方法,可以用when().thenCallRealMethod()
        when(service.light()).thenCallRealMethod();
        assertThat(service.light(),equalTo(999));

        //mock出的对象不真实调用其方法的写法和原来一样
        when(service.weight()).thenReturn("suc");
        assertThat(service.weight(),equalTo("suc"));
    }

4 Spy

spy不做处理时会真正调用函数,stub后会不真正调用函数而是返回指定结果。

4.1 方法一:MockByRunner

    @Test
    public void test(){
        ArrayList<String> realList = new ArrayList<>();
        ArrayList<String> list = spy(realList);
        list.add("A");
        list.add("B");
        assertThat(list.get(0),equalTo("A"));
        assertThat(list.get(1),equalTo("B"));
        assertThat(list.isEmpty(),equalTo(false));
        assertThat(list.size(),equalTo(2));

        //对部分方法进行stub使其不调用真实函数
        when(list.isEmpty()).thenReturn(true);
        when(list.size()).thenReturn(0);
        assertThat(list.isEmpty(),equalTo(true));
        assertThat(list.size(),equalTo(0));
    }

4.2 MockByAnnotation

其实就是把

        ArrayList<String> realList = new ArrayList<>();
        ArrayList<String> list = spy(realList);

合并成一步:

    @Spy
    ArrayList<String> list = new ArrayList<>();

完整代码如下:

public class SpyAnnotation {
    @Spy
    ArrayList<String> list = new ArrayList<>();

    @Before
    public void init(){
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void test(){
        list.add("A");
        list.add("B");
        assertThat(list.get(0),equalTo("A"));
        assertThat(list.get(1),equalTo("B"));
        assertThat(list.isEmpty(),equalTo(false));
        assertThat(list.size(),equalTo(2));

        //对部分方法进行stub使其不调用真实函数
        when(list.isEmpty()).thenReturn(true);
        when(list.size()).thenReturn(0);
        assertThat(list.isEmpty(),equalTo(true));
        assertThat(list.size(),equalTo(0));
    }
}

5 isA()和any()

stub用isA()时,assert会校验类型;stub用any()时,assert不会校验类型。

package com.lanshang.mockito.lesson7;

import org.junit.Test;
import org.mockito.Mock;
import org.mockito.Mockito;

import static javafx.beans.binding.Bindings.when;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.isA;

public class Args {
    @Test
    public void test(){
        Foo foo = Mockito.mock(Foo.class);
        Mockito.when(foo.get(isA(Parent.class))).thenReturn(100);
        assertThat(foo.get(new Child1()),equalTo(100));
        assertThat(foo.get(new Child2()),equalTo(100));

        //any(Child1.class)不会真的校验Child1.class类型,只要assert里foo.get()的参数类型是对的就能成功
        Mockito.when(foo.get(any(Child1.class))).thenReturn(300);
        assertThat(foo.get(new Child2()),equalTo(300));
    }
}

class Foo{
    public int get(Parent parent){
        return parent.fun();
    }
}

interface Parent{
    public int fun();
}

class Child1 implements Parent{
    @Override
    public int fun() {
        throw new RuntimeException();
    }
}

class Child2 implements Parent{
    @Override
    public int fun() {
        throw new RuntimeException();
    }
}

6 参数通配

待测试类
注意:SimpleService simpleService已经mock。

class SimpleService{
    public int method1(int i, String s, Collection c, Serializable ser){
        throw new RuntimeException();
    }

    public void method2(int i, String s, Collection c, Serializable ser){
        throw new RuntimeException();
    }
}

6.1 有返回值的函数

有返回值的函数用assert验证执行结果。

    @Test
    public void test1Method1(){
        Mockito.when(simpleService.method1(anyInt(),anyString(),anyCollection(),anyObject()))
                .thenReturn(100);
        Mockito.when(simpleService.method1(anyInt(),eq("a"),anyCollection(),anyObject()))
                .thenReturn(1);
        Mockito.when(simpleService.method1(anyInt(),eq("b"),anyCollection(),anyObject()))
                .thenReturn(2);
        assertThat(simpleService.method1(1,"c", Collections.emptyList(),"aaaa"),equalTo(100));
        assertThat(simpleService.method1(1,"b", Collections.emptyList(),"aaaa"),equalTo(2));
        assertThat(simpleService.method1(1,"a", Collections.emptyList(),"aaaa"),equalTo(1));
    }

注意:按下面这种顺序的mock,第三个mock会覆盖前两个mock

    @Test
    public void testMethod1(){
        Mockito.when(simpleService.method1(anyInt(),eq("a"),anyCollection(),anyObject()))
                .thenReturn(1);
        Mockito.when(simpleService.method1(anyInt(),eq("b"),anyCollection(),anyObject()))
                .thenReturn(2);
        //注意,按这种顺序的mock,第三个mock会覆盖前两个mock
        Mockito.when(simpleService.method1(anyInt(),anyString(),anyCollection(),anyObject()))
                .thenReturn(100);
        assertThat(simpleService.method1(1,"c", Collections.emptyList(),"aaaa"),equalTo(100));
        assertThat(simpleService.method1(1,"b", Collections.emptyList(),"aaaa"),equalTo(100));
        assertThat(simpleService.method1(1,"a", Collections.emptyList(),"aaaa"),equalTo(100));
    }

6.2 无返回值的函数

无返回值的函数用verify验证执行结果。

    @Test
    public void test1Method2(){
        Mockito.doNothing().when(simpleService).method2(anyInt(),anyString(),anyCollection(),isA(Serializable.class));
        simpleService.method2(1,"a", Collections.emptyList(),"aaaa");
        Mockito.verify(simpleService,times(1)).method2(1,"a", Collections.emptyList(),"aaaa");
    }

7 assertThat一些用法

    @Test
    public void test(){
        int i=10;
        assertThat(i,equalTo(10));
        assertThat(i,not(equalTo(20)));
        assertThat(i,is(10));
        assertThat(i,is(not(20)));
    }

    @Test
    public void test2(){
        int i=10;
        assertThat(i,both(equalTo(10)).and(not(equalTo(20))));
        assertThat(i,either(equalTo(10)).or(equalTo(20)));
        assertThat(i,anyOf(is(10),is(20),not(30)));
        assertThat(i,allOf(is(10),not(is(20)),not(30)));

        assertThat(i,is(not(20)));

        assertThat(Stream.of(1,2,3).anyMatch(j->j<2),equalTo(true));
        assertThat(Stream.of(1,2,3).allMatch(j->j<5),equalTo(true));

    }

8 注意点

8.1 不能mock局部变量

可以mock被测试方法所属类的成员变量,也可以moke被测试方法的入参,但是不能moke被测试方法的局部变量。moke被测试方法的局部变量会失效。

8.2 被测试的类中调用的函数的参数不需要mock

以测试类为基础,直接写在测试类的代码搞不到的就要mock,比如下面代码的这两行
String name = request.getParameter("name"); String password = request.getParameter("password");

package com.lanshang.mockito.quickstart;

import javax.servlet.http.HttpServletRequest;

public class AccountLoginController {
    AccountDao accountDao;

    public AccountLoginController(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    public String login(HttpServletRequest request){
        String name = request.getParameter("name");
        String password = request.getParameter("password");
        try {
            Account account = accountDao.findAccount(name,password);
            if(account == null){
                return "/login";
            }else{
                return "/index";
            }
        } catch (Exception e) {
            e.printStackTrace();
            return "/505";
        }

    }
}

测试类

package com.lanshang.mockito.quickstart;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;

import javax.servlet.http.HttpServletRequest;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsEqual.equalTo;

@RunWith(MockitoJUnitRunner.class)
public class AccountLoginControllerTest {
    AccountDao dao ;
    AccountLoginController accountLoginController ;
    HttpServletRequest request;
    Account account = new Account();



    @Before
    public void init(){
        dao = Mockito.mock(AccountDao.class);
        accountLoginController = new AccountLoginController(dao);
        request = Mockito.mock(HttpServletRequest.class);

    }

    @Test
    public void loginTest(){
        Mockito.when(request.getParameter("name")).thenReturn("cat");
        Mockito.when(request.getParameter("password")).thenReturn("123456");
        Mockito.when(dao.findAccount(Mockito.anyString(),Mockito.anyString())).thenReturn(account);
        String login = accountLoginController.login(request);
        assertThat(login,equalTo("/index"));
    }
}

但如果String name = request.getParameter("name"); String password = request.getParameter("password");这两行代码没有单独写,而是作为被测试的代码中某个调用函数的入参,如下图: Account account = accountDao.findAccount(request.getParameter("name"),request.getParameter("password"));
则可以不用mock,测试到调用函数这一行时入参用any代替就好了
被测试代码:

package com.lanshang.mockito.quickstart;

import javax.servlet.http.HttpServletRequest;

public class AccountLoginController {
    AccountDao accountDao;

    public AccountLoginController(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    public String login(HttpServletRequest request){

        try {
            Account account = accountDao.findAccount(request.getParameter("name"),request.getParameter("password"));
            if(account == null){
                return "/login";
            }else{
                return "/index";
            }
        } catch (Exception e) {
            e.printStackTrace();
            return "/505";
        }

    }
}

测试代码

package com.lanshang.mockito.quickstart;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;

import javax.servlet.http.HttpServletRequest;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsEqual.equalTo;

@RunWith(MockitoJUnitRunner.class)
public class AccountLoginControllerTest {
    AccountDao dao ;
    AccountLoginController accountLoginController ;
    HttpServletRequest request;
    Account account = new Account();



    @Before
    public void init(){
        dao = Mockito.mock(AccountDao.class);
        accountLoginController = new AccountLoginController(dao);
        request = Mockito.mock(HttpServletRequest.class);

    }

    @Test
    public void loginTest(){

        Mockito.when(dao.findAccount(Mockito.anyString(),Mockito.anyString())).thenReturn(account);
        String login = accountLoginController.login(request);
        assertThat(login,equalTo("/index"));
    }
}

8.3 mock出的对象最好reset

mock出的对象最好reset下,在测试结束后清除stub造成的影响。下例simpleService是mock的对象。

    @After
    public void destroy(){
        Mockito.reset(simpleService);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值