Mockito之:深度mock+不打桩都mock了啥+我想用被mock了的类的真实方法+我mock了返回为void的函数怎么办+mock的方法返回为Except

282 阅读2分钟

Mockito之:深度mock+不打桩都mock了啥+我想用被mock了的类的真实方法+我mock了返回为void的函数怎么办+mock的方法返回为Exception怎么办

先把两个测试类摆在这里

package com.newcrud.service.impl;
​
import org.springframework.stereotype.Component;
​
@Component
public class MyMockTwoServiceImpl {
    //这个是为了验证深度mock
    public Integer getTwo(Integer id){
        return id;
    }
}
package com.newcrud.service.impl;
​
import org.springframework.stereotype.Component;
​
import java.util.List;
import java.util.Map;
​
@Component
public class MyMockOneServiceImpl {
    public Integer getInt(Integer id){
        return id;
    }
    public String getString(String string){
        return string;
    }
    public Double getDouble(Double dd){
        return dd;
    }
    public Long getLong(Long ll){
        return ll;
    }
    public int[] getIntShuZu(int[] a){
        return a;
    }
    public List<Integer> getListInt(List<Integer> list){
        return list;
    }
    public List<String> getListString(List<String> list){
        return list;
    }
    public Map<Integer,String> getMap(Map<Integer,String> map){
        return map;
    }
    public void getVoid(){
        System.out.println("getVoid");
    }
    public RuntimeException getException(){
        RuntimeException exception=new RuntimeException();
        return exception;
    }
    public MyMockTwoServiceImpl getMyMockTwoService(){
        MyMockTwoServiceImpl myMockTwoService=new MyMockTwoServiceImpl();
        return myMockTwoService;
    }
​
}

不打桩都mock了啥

下面这写test都是执行通过的。

总结一下:

返回值为int的mock结果为0

返回值为String的mock结果为null

返回值为double的mock结果为0.0

返回值为Long的mock结果为0L

返回值为int[]的mock结果为null

返回值为List的mock结果为[]

返回值为List的mock结果为[]

返回值为Map的mock结果为{}

package com.newcrud.service.impl;
​
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
​
import java.util.*;
​
import static org.testng.Assert.*;
​
@SpringBootTest
public class MyMockOneServiceImplTest extends AbstractTestNGSpringContextTests {
​
    @BeforeClass
    public void initMocks(){
        MockitoAnnotations.initMocks(this);
    }
​
    @Mock
    MyMockOneServiceImpl myMockOneService;
​
    @Test
    public void testGetInt() {
        Integer id =myMockOneService.getInt(999);
        Integer id2=0;
        assertEquals(id,id2);
    }
​
    @Test
    public void testGetString() {
        String s=myMockOneService.getString("zhangsan");
        assertEquals(s,null);
    }
​
    @Test
    public void testGetDouble() {
        Double dou =myMockOneService.getDouble(2.2);
        assertEquals(dou,0.0);
    }
​
    @Test
    public void testGetLong() {
        Long lon = myMockOneService.getLong(100000L);
        Long lon2 = 0L;
        assertEquals(lon2,lon);
    }
​
    @Test
    public void testGetIntShuZu(){
        int[] a=new int[]{1,2,3};
        assertEquals(null,myMockOneService.getIntShuZu(a));
    }
​
    @Test
    public void testGetListInt() {
        List<Integer> a=new ArrayList<Integer>();
        a.add(1);
        List<Integer> b=new ArrayList<Integer>();
        assertEquals(b,myMockOneService.getListInt(a));
    }
​
    @Test
    public void testGetListString() {
        List<String> a = new ArrayList<>();
        a.add("zhangsan");
        List<String> b=new ArrayList<>();
        assertEquals(b,myMockOneService.getListString(a));
    }
​
    @Test
    public void testGetMap() {
        Map<Integer,String> map=new HashMap<>();
        map.put(1,"zhangsan");
        Map map2=new HashMap();
        assertEquals(map2,myMockOneService.getMap(map));
    }
}

深度mock

我们先来演示一下问题

package com.newcrud.service.impl;
​
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
​
/**
 * 现状:MyMockOneServiceImpl的getMyMockTwoService方法可以获取到一个MyMockTwoServiceImpl对象,MyMockTwoServiceImpl有一个getTwo方法
 * 需求:我要mock一个MyMockOneServiceImpl,调用getMyMockTwoService生成一个MyMockTwoServiceImpl对象去执行getTwo方法
 * **/
@SpringBootTest
public class MyMockOneServiceImplTest extends AbstractTestNGSpringContextTests {
​
    @BeforeClass
    public void initMocks(){
        MockitoAnnotations.initMocks(this);
    }
​
    @Mock
    MyMockOneServiceImpl myMockOneService;
    @Test
    public void testDeepMock(){
        MyMockTwoServiceImpl myMockTwoService=myMockOneService.getMyMockTwoService();
        Assert.assertEquals(null,myMockTwoService);
        //结果:测试通过
    }
    @Test
    public void testDeepMock2(){
        MyMockTwoServiceImpl myMockTwoService=myMockOneService.getMyMockTwoService();
        myMockTwoService.getTwo(8);
        //结果:测试失败
    }
}

testDeepMock2结果:

java.lang.NullPointerException
  at com.newcrud.service.impl.MyMockOneServiceImplTest.testDeepMock2(MyMockOneServiceImplTest.java:35)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:498)
  at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:104)
  at org.testng.internal.MethodInvocationHelper$1.runTestMethod(MethodInvocationHelper.java:205)
  at org.springframework.test.context.testng.AbstractTestNGSpringContextTests.run(AbstractTestNGSpringContextTests.java:184)
  at org.testng.internal.MethodInvocationHelper.invokeHookable(MethodInvocationHelper.java:217)
  at org.testng.internal.Invoker.invokeMethod(Invoker.java:641)
  at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:851)
  at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1177)
  at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:129)
  at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:112)
  at org.testng.TestRunner.privateRun(TestRunner.java:756)
  at org.testng.TestRunner.run(TestRunner.java:610)
  at org.testng.SuiteRunner.runTest(SuiteRunner.java:387)
  at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:382)
  at org.testng.SuiteRunner.privateRun(SuiteRunner.java:340)
  at org.testng.SuiteRunner.run(SuiteRunner.java:289)
  at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
  at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
  at org.testng.TestNG.runSuitesSequentially(TestNG.java:1293)
  at org.testng.TestNG.runSuitesLocally(TestNG.java:1218)
  at org.testng.TestNG.runSuites(TestNG.java:1133)
  at org.testng.TestNG.run(TestNG.java:1104)
  at com.intellij.rt.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:66)
  at com.intellij.rt.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:109)

分析:通过testDeepMock我们也知道了我们mock出来的myMockOneService,执行getMyMockTwoService方法,返回的是一个null对不对,那一个null去执行getTwo方法,那一定会报空指针

改进方法1

package com.newcrud.service.impl;
​
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
​
import static org.mockito.Mockito.when;
​
/**
 * 现状:MyMockOneServiceImpl的getMyMockTwoService方法可以获取到一个MyMockTwoServiceImpl对象,MyMockTwoServiceImpl有一个getTwo方法
 * 需求:我要mock一个MyMockOneServiceImpl,调用getMyMockTwoService生成一个MyMockTwoServiceImpl对象去执行getTwo方法
 * **/
@SpringBootTest
public class MyMockOneServiceImplTest extends AbstractTestNGSpringContextTests {
​
    @BeforeClass
    public void initMocks(){
        MockitoAnnotations.initMocks(this);
    }
​
    @Mock
    MyMockOneServiceImpl myMockOneService;
    @Test
    public void testDeepMock(){
        MyMockTwoServiceImpl myMockTwoService=myMockOneService.getMyMockTwoService();
        Assert.assertEquals(null,myMockTwoService);
        //结果:测试通过
    }
    @Test
    public void testDeepMock2(){
        MyMockTwoServiceImpl myMockTwoService1=new MyMockTwoServiceImpl();
        when(myMockOneService.getMyMockTwoService()).thenReturn(myMockTwoService1);
        //MyMockTwoServiceImpl myMockTwoService=myMockOneService.getMyMockTwoService();
        //myMockTwoService.getTwo(8);
        myMockTwoService1.getTwo(88);
        //结果:测试失败
    }
}

结果:执行通过,但是还是很麻烦,这要是MyMockTwoServiceImpl还有MyMockThreeServiceImpl怎么办?

改进方法二:DeepMock

package com.newcrud.service.impl;
​
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
​
import static org.mockito.Mockito.when;
​
/**
 * 现状:MyMockOneServiceImpl的getMyMockTwoService方法可以获取到一个MyMockTwoServiceImpl对象,MyMockTwoServiceImpl有一个getTwo方法
 * 需求:我要mock一个MyMockOneServiceImpl,调用getMyMockTwoService生成一个MyMockTwoServiceImpl对象去执行getTwo方法
 * **/
@SpringBootTest
public class MyMockOneServiceImplTest extends AbstractTestNGSpringContextTests {
​
    @BeforeClass
    public void initMocks(){
        MockitoAnnotations.initMocks(this);
    }
​
    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
    MyMockOneServiceImpl myMockOneService;
    @Test
    public void testDeepMock(){
        int i=myMockOneService.getMyMockTwoService().getTwo(99);
        int j=0;
        MyMockTwoServiceImpl myMockTwoService=myMockOneService.getMyMockTwoService();
        Assert.assertEquals(i,j);
        //结果:测试通过
    }
}

emmmm,看起来清爽多了

我想用被mock了的类的真实方法

被mock的类,有10个方法,我只想mock其中一个

package com.newcrud.service.impl;
​
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.*;
​
@SpringBootTest
public class MyMockOneServiceImplTest extends AbstractTestNGSpringContextTests {
​
    @BeforeClass
    public void initMocks(){
        MockitoAnnotations.initMocks(this);
    }
​
    @Mock(answer = Answers.CALLS_REAL_METHODS)
    MyMockOneServiceImpl myMockOneService;
    @Test
    public void testDoRealMethod() {
        //请求的是真实的myMockOneService的方法,否则应该返回的是0
        int i=myMockOneService.getInt(99);
        int j=99;
        Assert.assertEquals(i,j);
        //打桩,不打桩应该返回的是nice
        when(myMockOneService.getString(anyString())).thenReturn("hello");
        String s=myMockOneService.getString("nice");
        Assert.assertEquals(s,"hello");
        //测试通过
    }
}

被mock的类,有10个方法,我想mock其中9个,只想其中一个调用真实方法

package com.newcrud.service.impl;

import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.*;

@SpringBootTest
public class MyMockOneServiceImplTest extends AbstractTestNGSpringContextTests {

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

    @Mock
    MyMockOneServiceImpl myMockOneService;
    @Test
    public void testDoRealMethod() {
        when(myMockOneService.getString(anyString())).thenCallRealMethod();
        //这里调用的是真实的方法,输入什么返回什么
        String s=myMockOneService.getString("zhangsan");
        String ss="zhangsan";
        Assert.assertEquals(s,ss);
        //测试通过
    }
    @Test
    public void testDoRealMethod2() {
        //这里调用的就是mock的方法,应该返回的是0
        Integer i = myMockOneService.getInt(99);
        Integer j=0;
        Assert.assertEquals(i,j);
        //测试通过
    }
}

我mock了返回为void的函数怎么办

返回了void,我又该怎么确定它是不是被执行了呢

package com.newcrud.service.impl;

import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.*;

@SpringBootTest
public class MyMockOneServiceImplTest extends AbstractTestNGSpringContextTests {

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

    @Mock
    MyMockOneServiceImpl myMockOneService;
    @Test
    public void testGetVoid() {
        //注意,被mock的类在when里了,方法在when外面
        doNothing().when(myMockOneService).getVoid();
        myMockOneService.getVoid();
        //verify(被mock的类,times(期望被执行的次数)).被打桩的方法
        verify(myMockOneService,times(1)).getVoid();
        //测试通过
    }
}

我们再来看一下失败的时候是什么样子的

package com.newcrud.service.impl;

import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.*;

@SpringBootTest
public class MyMockOneServiceImplTest extends AbstractTestNGSpringContextTests {

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

    @Mock
    MyMockOneServiceImpl myMockOneService;
    @Test
    public void testGetVoid() {
        //注意,被mock的类在when里了,方法在when外面
        doNothing().when(myMockOneService).getVoid();
        myMockOneService.getVoid();
        //verify(被mock的类,times(期望被执行的次数)).被打桩的方法
        verify(myMockOneService,times(2)).getVoid();
        //测试通过
    }
}

结果

org.mockito.exceptions.verification.TooLittleActualInvocations: 
myMockOneService.getVoid();
Wanted 2 times:
-> at com.newcrud.service.impl.MyMockOneServiceImplTest.testGetVoid(MyMockOneServiceImplTest.java:30)
But was 1 time:
-> at com.newcrud.service.impl.MyMockOneServiceImplTest.testGetVoid(MyMockOneServiceImplTest.java:28)

mock的方法返回为Exception怎么办

我想要测试这个方法返回了一个Exception,但是Exception又有很多种,怎么办

期望Exception=抛出Exception

package com.newcrud.service.impl;

import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.when;

@SpringBootTest
public class MyMockOneServiceImplTest extends AbstractTestNGSpringContextTests {

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

    @Mock
    MyMockOneServiceImpl myMockOneService;
    @Test(expectedExceptions = RuntimeException.class)
    public void testGetException() {
        when(myMockOneService.getException()).thenThrow(new RuntimeException());
        myMockOneService.getException();
        //测试结果:通过
    }
}

期望Exception!=抛出Exception

package com.newcrud.service.impl;

import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.when;

@SpringBootTest
public class MyMockOneServiceImplTest extends AbstractTestNGSpringContextTests {

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

    @Mock
    MyMockOneServiceImpl myMockOneService;
    @Test(expectedExceptions = NullPointerException.class)
    public void testGetException() {
        when(myMockOneService.getException()).thenThrow(new RuntimeException());
        myMockOneService.getException();
        //测试结果:失败
    }
}

期望Exception>抛出Exception

package com.newcrud.service.impl;

import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.when;

@SpringBootTest
public class MyMockOneServiceImplTest extends AbstractTestNGSpringContextTests {

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

    @Mock
    MyMockOneServiceImpl myMockOneService;
    @Test(expectedExceptions = Exception.class)
    public void testGetException() {
        when(myMockOneService.getException()).thenThrow(new RuntimeException());
        myMockOneService.getException();
        //测试结果:通过
    }
}