前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Mocktio 使用(下)

Mocktio 使用(下)

原创
作者头像
HLee
修改2021-10-13 17:52:08
3.3K0
修改2021-10-13 17:52:08
举报
文章被收录于专栏:房东的猫房东的猫房东的猫

使用 thenReturn、doReturn设置方法的返回值

thenReturn 用来指定特定函数和参数调用的返回值。thenReturn 中可以指定多个返回值,在调用时返回值依次出现。若调用次数超过返回值的数量,再次调用时返回最后一个返回值。

import org.junit.Assert;
import org.junit.Test;
import static org.mockito.Mockito.*;
import org.mockito.MockitoAnnotations;

import java.util.Random;

public class MockitoTest7 {

    @Test
    public void test() {

        Random mockRandom = mock(Random.class);

        when(mockRandom.nextInt()).thenReturn(1);
        Assert.assertEquals(1, mockRandom.nextInt());
    }

    @Test
    public void test2() {
        Random mockRandom = mock(Random.class);

        when(mockRandom.nextInt()).thenReturn(1, 2, 3);

        Assert.assertEquals(1, mockRandom.nextInt());
        Assert.assertEquals(2, mockRandom.nextInt());
        Assert.assertEquals(3, mockRandom.nextInt());
        Assert.assertEquals(3, mockRandom.nextInt());
        Assert.assertEquals(3, mockRandom.nextInt());
    }

    @Test
    public void test3() {

        MockitoAnnotations.initMocks(this);

        Random random = mock(Random.class);
        doReturn(1).when(random).nextInt();

        Assert.assertEquals(1, random.nextInt());
    }
}

doReturn 的作用和thenReturn相同,但使用方式不同:

使用 thenThrow、doThrow让方法抛出异常

thenThrow 用来让函数调用抛出异常。thenThrow 中可以指定多个异常,在调用时异常依次出现。若调用次数超过异常的数量,再次调用时抛出最后一个异常。

import org.junit.Assert;
import org.junit.Test;
import static org.mockito.Mockito.*;

import java.util.Random;

public class MockitoTest8 {

    @Test
    public void test() {

        Random mockRandom = mock(Random.class);

        when(mockRandom.nextInt()).thenThrow(new RuntimeException("异常"));

        try {
            mockRandom.nextInt();
            Assert.fail();  // 上面会抛出异常,所以不会走到这里
        } catch (Exception ex) {
            System.out.println("RuntimeException");
            Assert.assertTrue(ex instanceof RuntimeException);
            Assert.assertEquals("异常", ex.getMessage());
        }
    }

    @Test
    public void test2() {

        Random mockRandom = mock(Random.class);

        when(mockRandom.nextInt()).thenThrow(new RuntimeException("异常1"), new RuntimeException("异常2"));

        try {
            mockRandom.nextInt();
            Assert.fail();
        } catch (Exception ex) {
            System.out.println("RuntimeException 1");
            Assert.assertTrue(ex instanceof RuntimeException);
            Assert.assertEquals("异常1", ex.getMessage());
        }

        try {
            mockRandom.nextInt();
            Assert.fail();
        } catch (Exception ex) {
            System.out.println("RuntimeException 2");
            Assert.assertTrue(ex instanceof RuntimeException);
            Assert.assertEquals("异常2", ex.getMessage());
        }
    }
}

如果一个对象的方法的返回值是 void,那么不能用 when .. thenThrow 让该方法抛出异常。用 doThrow 可以让返回void的函数抛出异常。

public class MockitoTest10 {

    static class ExampleService {

        public void hello() {
            System.out.println("Hello");
        }
    }

    @Mock
    private ExampleService exampleService;

    @Test
    public void test() {

        MockitoAnnotations.initMocks(this);

        // 这种写法可以达到效果
        doThrow(new RuntimeException("异常")).when(exampleService).hello();

        try {
            exampleService.hello();
            Assert.fail();
        } catch (RuntimeException ex) {
            Assert.assertEquals("异常", ex.getMessage());
        }
    }

    @Test
    public void test2() {

        MockitoAnnotations.initMocks(this);

        Random random = mock(Random.class);

        // 下面这句等同于 when(random.nextInt()).thenThrow(new RuntimeException("异常"));
        doThrow(new RuntimeException("异常")).when(random).nextInt();

        try {
            random.nextInt();
            Assert.fail();
        } catch (RuntimeException ex) {
            Assert.assertEquals("异常", ex.getMessage());
        }
    }
}

使用then、thenAnswer、doAnswer自定义方法处理逻辑

then 和 thenAnswer 的效果是一样的。它们的参数是实现 Answer 接口的对象,在改对象中可以获取调用参数,自定义返回值。

import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.when;

import static org.mockito.Mockito.*;

public class MockitoTest9 {

    static class ExampleService {

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

    @Mock
    private ExampleService exampleService;

    @Test
    public void test() {

        MockitoAnnotations.initMocks(this);

        when(exampleService.add(anyInt(),anyInt())).thenAnswer(new Answer<Integer>() {
            @Override
            public Integer answer(InvocationOnMock invocation) throws Throwable {
                Object[] args = invocation.getArguments();
                // 获取参数
                Integer a = (Integer) args[0];
                Integer b = (Integer) args[1];

                // 根据第1个参数,返回不同的值
                if (a == 1) {
                    return 9;
                }
                if (a == 2) {
                    return 99;
                }
                if (a == 3) {
                    throw new RuntimeException("异常");
                }
                return 999;
            }
        });

        Assert.assertEquals(9, exampleService.add(1, 100));
        Assert.assertEquals(99, exampleService.add(2, 100));

        try {
            exampleService.add(3, 100);
            Assert.fail();
        } catch (RuntimeException ex) {
            Assert.assertEquals("异常", ex.getMessage());
        }
    }

    // doAnswer 的作用和 thenAnswer 相同,但使用方式不同:
    @Test
    public void test2() {

        MockitoAnnotations.initMocks(this);

        Random random = mock(Random.class);
        doAnswer(new Answer() {
            @Override
            public Object answer(InvocationOnMock invocation) throws Throwable {
                return 1;
            }
        }).when(random).nextInt();

        Assert.assertEquals(1, random.nextInt());
    }
}

使用 doNothing 让 void 函数什么都不做

doNothing 用于让 void 函数什么都不做。因为 mock 对象中,void 函数就是什么都不做,所以该方法更适合 spy 对象。

import static org.mockito.Mockito.*;

import org.junit.Test;

public class MockitoTest11 {

    static class ExampleService11 {

        public void hello() {
            System.out.println("Hello");
        }
    }

    @Test
    public void test() {

        ExampleService11 exampleService = spy(new ExampleService11());
        exampleService.hello();  // 会输出 Hello

        // 让 hello 什么都不做
        doNothing().when(exampleService).hello();
        exampleService.hello(); // 什么都不输出
    }
}

使用 reset 重置对象

使用 reset 方法,可以重置之前自定义的返回值和异常。

import org.junit.Test;

import org.junit.Assert;

import static org.mockito.Mockito.*;

public class MockitoTest12 {

    static class ExampleService {

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

    @Test
    public void test() {

        ExampleService exampleService = mock(ExampleService.class);

        // mock 对象方法的默认返回值是返回类型的默认值
        Assert.assertEquals(0, exampleService.add(1, 2));

        // 设置让 add(1,2) 返回 100
        when(exampleService.add(1, 2)).thenReturn(100);
        Assert.assertEquals(100, exampleService.add(1, 2));

        // 重置 mock 对象,add(1,2) 返回 0
        reset(exampleService);
        Assert.assertEquals(0, exampleService.add(1, 2));
    }

    @Test
    public void test2() {

        ExampleService exampleService = spy(new ExampleService());

        // spy 对象方法调用会用真实方法,所以这里返回 3
        Assert.assertEquals(3, exampleService.add(1, 2));

        // 设置让 add(1,2) 返回 100
        when(exampleService.add(1, 2)).thenReturn(100);
        Assert.assertEquals(100, exampleService.add(1, 2));

        // 重置 spy 对象,add(1,2) 返回 3
        reset(exampleService);
        Assert.assertEquals(3, exampleService.add(1, 2));
    }
}

使用 thenCallRealMethod 调用 spy 真实方法

thenCallRealMethod 可以用来重置 spy 对象的特定方法特定参数调用。

import org.junit.Assert;
import org.junit.Test;

import static org.mockito.Mockito.*;

/**
 */
public class MockitoTest13 {

    static class ExampleService {

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

    @Test
    public void test() {

        ExampleService exampleService = spy(new ExampleService());

        // spy 对象方法调用会用真实方法,所以这里返回 3
        Assert.assertEquals(3, exampleService.add(1, 2));

        // 设置让 add(1,2) 返回 100
        when(exampleService.add(1, 2)).thenReturn(100);
        when(exampleService.add(2, 2)).thenReturn(100);
        Assert.assertEquals(100, exampleService.add(1, 2));
        Assert.assertEquals(100, exampleService.add(2, 2));

        // 重置 spy 对象,让 add(1,2) 调用真实方法,返回 3
        when(exampleService.add(1, 2)).thenCallRealMethod();
        Assert.assertEquals(3, exampleService.add(1, 2));

        // add(2, 2) 还是返回 100
        Assert.assertEquals(100, exampleService.add(2, 2));
    }
}

使用 verify 校验是否发生过某些操作

  • 使用 verify 可以校验 mock 对象是否发生过某些操作;
  • 使用 verify 配合 time 方法,可以校验某些操作发生的次数;
import org.junit.Test;

import static org.mockito.Mockito.*;

public class MockitoTest14 {

    static class ExampleService {

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

    @Test
    public void test() {

        ExampleService exampleService = mock(ExampleService.class);

        // 设置让 add(1,2) 返回 100
        when(exampleService.add(1, 2)).thenReturn(100);

        exampleService.add(1, 2);

        // 校验是否调用过 add(1, 2) -> 校验通过
        verify(exampleService).add(1, 2);

        // 校验是否调用过 add(2, 2) -> 校验不通过
        verify(exampleService).add(2, 2);
    }

    @Test
    public void test2() {

        ExampleService exampleService = mock(ExampleService.class);

        // 第1次调用
        exampleService.add(1, 2);

        // 校验是否调用过一次 add(1, 2) -> 校验通过
        verify(exampleService, times(1)).add(1, 2);

        // 第2次调用
        exampleService.add(1, 2);

        // 校验是否调用过两次 add(1, 2) -> 校验通过
        verify(exampleService, times(2)).add(1, 2);
    }
}

使用 mockingDetails 判断是否为 mock对象、spy对象

Mockito 的 mockingDetails 方法会返回 MockingDetails 对象,它的 isMock 方法可以判断对象是否为 mock 对象,isSpy 方法可以判断对象是否为 spy 对象。

public class MockitoTest15 {

    static class ExampleService {

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

    @Test
    public void test() {

        ExampleService exampleService = mock(ExampleService.class);

        // 判断 exampleService 是否为 mock 对象
        System.out.println( mockingDetails(exampleService).isMock() );     // true

        // 判断 exampleService 是否为 spy 对象
        System.out.println( mockingDetails(exampleService).isSpy() );      // false
    }
}

Mockito 测试隔离

使用 @Mock 修饰的 mock 对象在不同的单测中会被隔离开。

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class MockitoTest16 {

    static class ExampleService {

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

    @Mock
    private ExampleService exampleService;

    @Test
    public void test01() {
        System.out.println("---call test01---");

        System.out.println("打桩前: " + exampleService.add(1, 2));

        when(exampleService.add(1, 2)).thenReturn(100);

        System.out.println("打桩后: " + exampleService.add(1, 2));
    }

    @Test
    public void test02() {
        System.out.println("---call test02---");

        System.out.println("打桩前: " + exampleService.add(1, 2));

        when(exampleService.add(1, 2)).thenReturn(100);

        System.out.println("打桩后: " + exampleService.add(1, 2));
    }
}

执行结果:
---call test01---
打桩前: 0
打桩后: 100
---call test02---
打桩前: 0
打桩后: 100

test01 先被执行,打桩前调用add(1, 2)的结果是0,打桩后是 100。然后 test02 被执行,打桩前调用add(1, 2)的结果是0,而非 100,这证明了我们上面的说法。

使用 PowerMock 让 Mockito 支持静态方法

PowerMock 是一个增强库,用来增加 Mockito 、EasyMock 等测试库的功能。

Mockito 默认是不支持静态方法,比如我们在 ExampleService 类中定义静态方法 add,尝试给静态方法打桩,会报错。

可以用 Powermock 弥补 Mockito 缺失的静态方法 mock 功能。在 pom 中配置以下依赖:

<dependency>
	<groupId>org.mockito</groupId>
	<artifactId>mockito-core</artifactId>
	<version>2.25.1</version>
</dependency>
<dependency>
	<groupId>org.powermock</groupId>
	<artifactId>powermock-core</artifactId>
	<version>2.0.0</version>
</dependency>
<dependency>
	<groupId>org.powermock</groupId>
	<artifactId>powermock-module-junit4</artifactId>
	<version>2.0.0</version>
</dependency>
<dependency>
	<groupId>org.powermock</groupId>
	<artifactId>powermock-api-mockito2</artifactId>
	<version>2.0.0</version>
</dependency>

PowerMockRunner 支持 Mockito 的 @Mock 等注解。

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import java.util.Random;

import static org.mockito.Mockito.*;

/**
 * @author Huan Lee
 * @version 1.0
 * @date 10/13/21 4:42 PM
 * @describtion 业精于勤,荒于嬉;行成于思,毁于随。
 */
@RunWith(PowerMockRunner.class)     // 这是必须的
@PrepareForTest(ExampleService17.class)  // 声明要处理 ExampleService17
public class MockitoTest17 {

    @Mock
    private Random random;

    @Test
    public void test() {

        PowerMockito.mockStatic(ExampleService17.class);  // 这也是必须的

        when(ExampleService17.add(1, 2)).thenReturn(100);

        Assert.assertEquals(100, ExampleService17.add(1, 2));
        Assert.assertEquals(0, ExampleService17.add(2, 2));
    }

    @Test
    public void test2() {

        when(random.nextInt()).thenReturn(1);
        Assert.assertEquals(1,  random.nextInt());
    }
}

class ExampleService17 {

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

临时 mock 对象

如果需要临时将一个对象的内部对象替换为 mock 对象,在无法通过set和get处理内部对象的情况下,可以利用反射搞定。

Java JOOR 反射库 是一个很好用的反射库。本文用它进行临时替换。

<dependency>
	<groupId>org.jooq</groupId>
	<artifactId>joor-java-8</artifactId>
	<version>0.9.7</version>
</dependency>
public class HttpService {

    public int queryStatus() {
        // 发起网络请求,提取返回结果
        // 这里直接返回0
        return 0;
    }
}
public class BizService {

    private HttpService httpService = new HttpService();

    public String hello() {
        int status = httpService.queryStatus();
        if (status == 0) {
            return "你好";
        }
        else if (status == 1) {
            return "Hello";
        }
        else {
            return "未知状态";
        }
    }
}
import org.joor.Reflect;
import org.junit.Test;

import static org.mockito.Mockito.*;

public class BizServiceTest {

    private BizService bizService = new BizService();

    @Test
    public void testHello() {

        System.out.println( bizService.hello() );  // 输出'你好'

        // 取出原有的对象
        Object realHttpService = Reflect.on(bizService).get("httpService");

        // 创建 mock 对象,并用它替换掉 bizService 中的 httpService 对象
        HttpService mockHttpService = mock(HttpService.class);
        when(mockHttpService.queryStatus()).thenReturn(1);
        Reflect.on(bizService).set("httpService", mockHttpService);

        System.out.println( bizService.hello() );  // 输出'hello'

        // 再将原先的对象设置回去
        Reflect.on(bizService).set("httpService", realHttpService);
        System.out.println( bizService.hello() );  // 输出'你好'
    }
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 使用 thenReturn、doReturn设置方法的返回值
  • 使用 thenThrow、doThrow让方法抛出异常
  • 使用then、thenAnswer、doAnswer自定义方法处理逻辑
  • 使用 doNothing 让 void 函数什么都不做
  • 使用 reset 重置对象
  • 使用 thenCallRealMethod 调用 spy 真实方法
  • 使用 verify 校验是否发生过某些操作
  • 使用 mockingDetails 判断是否为 mock对象、spy对象
  • Mockito 测试隔离
  • 使用 PowerMock 让 Mockito 支持静态方法
  • 临时 mock 对象
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档