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

Mocktio 使用(上)

原创
作者头像
HLee
修改于 2021-10-11 10:06:57
修改于 2021-10-11 10:06:57
1.8K00
代码可运行
举报
文章被收录于专栏:房东的猫房东的猫
运行总次数:0
代码可运行

简介

Mockito 是一个模拟测试框架。主要功能是模拟类/对象的行为。

Mockito 一般用于控制调用外部的返回值,让我们只关心和测试自己的业务逻辑。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>3.11.2</version>
    <scope>test</scope>
</dependency>

mock 方法模拟类和接口

org.mockito.Mockito 的 mock 方法可以模拟类和接口。

mock 类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import org.junit.Assert;
import org.junit.Test;
import java.util.Random;

import static org.mockito.Mockito.*;

public class MockitoDemo {

    @Test
    public void test() {
        Random mockRandom = mock(Random.class);
        when(mockRandom.nextInt()).thenReturn(100);  // 指定调用 nextInt 方法时,永远返回 100

        Assert.assertEquals(100, mockRandom.nextInt());
    }
}

注意,mock 对象的方法的返回值默认都是返回类型的默认值。例如,返回类型是 int,默认返回值是 0;返回类型是一个类,默认返回值是 null。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import org.junit.Assert;
import org.junit.Test;
import java.util.Random;

import static org.mockito.Mockito.*;

public class MockitoDemo {

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

        System.out.println mockRandom.nextBoolean());
        System.out.println(mockRandom.nextInt());
        System.out.println(mockRandom.nextDouble());
    }
}

返回结果:
false
0
0.0

mock 接口

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Test
public void test3() {
    List mockList = mock(List.class);

    Assert.assertEquals(0, mockList.size());
    Assert.assertEquals(null, mockList.get(0));

    // 调用 mock 对象的写方法,是没有效果的
    mockList.add("a");

    // 没有指定 size() 方法返回值,这里结果是默认值
    Assert.assertEquals(0, mockList.size());
    // 没有指定 get(0) 返回值,这里结果是默认值
    Assert.assertEquals(null, mockList.get(0));

    // 指定 get(0)时返回 a
    when(mockList.get(0)).thenReturn("a");

    // 没有指定 size() 方法返回值,这里结果是默认值
    Assert.assertEquals(0, mockList.size());
    // 因为上面指定了 get(0) 返回 a,所以这里会返回 a
    Assert.assertEquals("a", mockList.get(0));

    // 没有指定 get(1) 返回值,这里结果是默认值
    Assert.assertEquals(null, mockList.get(1));
}

@Mock 注解

@Mock 注解可以理解为对 mock 方法的一个替代。使用该注解时,要使用MockitoAnnotations.initMocks 方法,让注解生效。

MockitoAnnotations.initMocks放在 junit 的@Before注解修饰的函数中更合适。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.Random;

import static org.mockito.Mockito.*;

public class MockitoDemo {

    @Mock
    private Random random;

    @Before
    public void before() {
        // 让注解生效
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void test() {
        when(random.nextInt()).thenReturn(100);

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

MockitoAnnotations.initMocks的一个替代方案是使用 MockitoJUnitRunner

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

import java.util.Random;

import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class MockitoDemo {

    @Mock
    private Random random;

    @Test
    public void test() {
        when(random.nextInt()).thenReturn(100);
        Assert.assertEquals(100, random.nextInt());
    }
}

参数匹配

精确匹配

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class MockitoTest2 {

    @Mock
    private List<String> mockStringList;

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

    @Test
    public void test() {

        mockStringList.add("a");

        when(mockStringList.get(0)).thenReturn("a");
        when(mockStringList.get(1)).thenReturn("b");

        Assert.assertEquals("a", mockStringList.get(0));
        Assert.assertEquals("b", mockStringList.get(1));
    }

    @Test
    public void test2() {

        mockStringList.add("a");

        // 虽然可以用eq进行精确匹配,但是有点多余
        when(mockStringList.get(eq(0))).thenReturn("a");  
        when(mockStringList.get(eq(1))).thenReturn("b");

        Assert.assertEquals("a", mockStringList.get(0));
        Assert.assertEquals("b", mockStringList.get(1));

    }

}

模糊匹配

可以使用Mockito.anyInt()匹配所有类型为 int 的参数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class MockitoTest2 {

    @Mock
    private List<String> mockStringList;

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

    @Test
    public void test1() {

        mockStringList.add("a");

        // 使用 Mockito.anyInt() 匹配所有的 int
        when(mockStringList.get(anyInt())).thenReturn("a");

        Assert.assertEquals("a", mockStringList.get(0));
        Assert.assertEquals("a", mockStringList.get(1));
    }
}

anyInt 只是用来匹配参数的工具之一,目前 mockito 有多种匹配函数,部分如下:

函数名

匹配类型

any()

所有对象类型

anyInt()

基本类型 int、非 null 的 Integer 类型

anyChar()

基本类型 char、非 null 的 Character 类型

anyShort()

基本类型 short、非 null 的 Short 类型

anyBoolean()

基本类型 boolean、非 null 的 Boolean 类型

anyDouble()

基本类型 double、非 null 的 Double 类型

anyFloat()

基本类型 float、非 null 的 Float 类型

anyLong()

基本类型 long、非 null 的 Long 类型

anyByte()

基本类型 byte、非 null 的 Byte 类型

anyString()

String 类型(不能是 null)

anyList()

List<T> 类型(不能是 null)

anyMap()

Map<K, V>类型(不能是 null)

anyCollection()

Collection<T>类型(不能是 null)

anySet()

Set<T>类型(不能是 null)

any(Class<T> type)

type类型的对象(不能是 null)

isNull()

null

notNull()

非 null

isNotNull()

非 null

参数匹配顺序

如果参数匹配即声明了精确匹配,也声明了模糊匹配;又或者同一个值的精确匹配出现了两次,使用时会匹配哪一个?会匹配符合匹配条件的最新声明的匹配。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

import java.util.List;

import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class MockitoDemo {


    @Mock
    private List<String> testList;

    @Test
    public void test01() {

        // 精确匹配 0
        when(testList.get(0)).thenReturn("a");

        Assert.assertEquals("a", testList.get(0));

        // 精确匹配 0
        when(testList.get(0)).thenReturn("b");

        Assert.assertEquals("b", testList.get(0));

        // 模糊匹配
        when(testList.get(anyInt())).thenReturn("c");

        Assert.assertEquals("c", testList.get(0));
        Assert.assertEquals("c", testList.get(1));
    }

    @Test
    public void test02() {

        // 模糊匹配
        when(testList.get(anyInt())).thenReturn("c");

        Assert.assertEquals("c", testList.get(0));
        Assert.assertEquals("c", testList.get(1));

        // 精确匹配 0
        when(testList.get(0)).thenReturn("a");

        Assert.assertEquals("a", testList.get(0));
        Assert.assertEquals("c", testList.get(1));
    }
}

@Spy 注解

spy 和 mock不同,不同点是:

  • spy 的参数是对象示例,mock 的参数是 class
  • 被 spy 的对象,调用其方法时默认会走真实方法,mock 对象不会
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class MockitoTest4 {

    // 测试 spy
    @Test
    public void test_spy() {

        ExampleService spyExampleService = spy(new ExampleService());

        // 默认会走真实方法
        Assert.assertEquals(3, spyExampleService.add(1, 2));

        // 打桩后,不会走了
        when(spyExampleService.add(1, 2)).thenReturn(10);
        Assert.assertEquals(10, spyExampleService.add(1, 2));
    }

    // 测试 mock
    @Test
    public void test_mock() {

        ExampleService mockExampleService = mock(ExampleService.class);

        // 默认返回结果是返回类型int的默认值
        Assert.assertEquals(0, mockExampleService.add(1, 2));

    }

}

class ExampleService {

    int add(int a, int b) {
        System.out.println("add 方法");
        return a+b;
    }
}

spy对应注解 @Spy@Mock 是一样用的。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class MockitoTest5 {

    @Spy
    private ExampleService5 spyExampleService;

    @Test
    public void test_spy() {

        MockitoAnnotations.initMocks(this);

        Assert.assertEquals(3, spyExampleService.add(1, 2));

        when(spyExampleService.add(1, 2)).thenReturn(10);
        Assert.assertEquals(10, spyExampleService.add(1, 2));
    }
}

class ExampleService5 {

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

对于@Spy,如果发现修饰的变量是 null,会自动调用类的无参构造函数来初始化。所以下面两种写法是等价的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 写法1
@Spy
private ExampleService spyExampleService;

// 写法2
@Spy
private ExampleService spyExampleService = new ExampleService();

如果没有无参构造函数,必须使用写法2,例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import org.junit.Assert;
import org.junit.Test;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;

class ExampleService {

    private int a;

    public ExampleService(int a) {
        this.a = a;
    }

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

public class MockitoDemo {

    @Spy
    private ExampleService spyExampleService = new ExampleService(1);

    @Test
    public void test_spy() {

        MockitoAnnotations.initMocks(this);

        Assert.assertEquals(3, spyExampleService.add(2));
    }
}

@InjectMocks 注解注入mock对象

mockito 会将 @Mock@Spy 修饰的对象自动注入到 @InjectMocks 修饰的对象中。

注入方式有多种,mockito 会按照下面的顺序尝试注入:

  1. 构造函数注入
  2. 设值函数注入(set函数)
  3. 属性注入
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import java.util.Random;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import static org.mockito.Mockito.when;

public class MockitoTest6 {

    @Mock
    private HttpService6 httpService;

    @InjectMocks
    private ExampleService6 exampleService = new ExampleService6(); // 会将 httpService 注入进去

    @Test
    public void test01() {

        MockitoAnnotations.initMocks(this);

        when(httpService.queryStatus()).thenReturn(0);
        Assert.assertEquals("你好", exampleService.hello());
    }
}

class HttpService6 {

    public int queryStatus() {
        // 发起网络请求,提取返回结果
        // 这里用随机数模拟结果
        return new Random().nextInt(2);
    }
}

class ExampleService6 {

    private HttpService6 httpService;

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

https://www.letianbiji.com/java-mockito/mockito-mock.html

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

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

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

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

评论
登录后参与评论
暂无评论
推荐阅读
​LeetCode刷题实战119: 杨辉三角 II
https://leetcode-cn.com/problems/pascals-triangle-ii/
程序员小猿
2021/01/19
2060
​LeetCode刷题实战119: 杨辉三角 II
知识改变命运 数据结构【杨辉三角(顺序表)】
我们大概分析下题目根据画图可知,我们可以把每一行的元素进行存储,然后再把每一行存储起来,然后就实现了题目 代码:
用户11319080
2024/10/17
660
知识改变命运 数据结构【杨辉三角(顺序表)】
日拱一卒,月进一步(6)(杨辉三角2)
用户11039545
2024/05/04
740
​ LeetCode 118:杨辉三角 II Pascal's Triangle II
Given a non-negative index k where k ≤ 33, return the kth index row of the Pascal's triangle.
爱写bug
2019/07/07
3620
LeetCode-119. 杨辉三角II(java)
       这题我一开始拿到以为是直角形状的杨辉三角,其实不然,这题在我上题所解的非常类似​​《LeetCode118.杨辉三角》​​​。无非此题就是在上期的基础上,按要求返回指定行的数据进行返回。其实思路也很简单,你既然能按照指定的行数输出数据,那你也可以指定输出某一行的数据,对吧,你只需要最后在结果集上 ​​return list.get(rowIndex)​​即可。
bug菌
2023/05/27
1980
LeetCode-119. 杨辉三角II(java)
【Leetcode-118. 杨辉三角 -119. 杨辉三角Ⅱ】
题目:给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。
YoungMLet
2024/03/01
1250
《LeetCode-数组篇一》之杨辉三角与重塑矩阵
前言 本专栏是LeetCode刷题笔记,记录一下自己的做题轨迹,更好的让自己复习这些令人头痛的题目。博主是一个新手,做题水平非常有限,如有错敬请指出,如有对于题目有更优的解法也可以分享给博主,路漫漫其修远兮,算法之路慢慢而求索。
用户10517932
2023/10/07
1720
《LeetCode-数组篇一》之杨辉三角与重塑矩阵
三分钟看完两道套数学公式的算法题
题目来源于 LeetCode 上第 118 号问题:杨辉三角。题目难度为 Easy,目前通过率为 61.8% 。
五分钟学算法
2019/05/15
6430
三分钟看完两道套数学公式的算法题
118 杨辉三角
给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。在「杨辉三角」中,每个数是它左上方和右上方的数的和。
木瓜煲鸡脚
2021/11/30
3780
118 杨辉三角
【Java题解】杨辉三角—力扣
List<List<Integer>>是一个嵌套列表,列表里面存放的也是列表,且存放的列表里面存放的是Integer类型的对象。这类似与一个二维数组。
用户11162265
2024/08/05
920
【Java题解】杨辉三角—力扣
【C++经典例题】杨辉三角问题
“杨辉三角” 问题是一道经典的算法题目,它不仅考验对数组操作的熟练程度,还需要深入理解杨辉三角的数学特性。
倔强的石头_
2025/04/27
1630
【C++经典例题】杨辉三角问题
【杨辉三角】ArrayList和普通二维数组实现
前言: 小编这里有两种方法实现,但是其中一种是要有泛型,以及ArrayList和List有所了解的uu哦
用户11288949
2024/09/24
670
【杨辉三角】ArrayList和普通二维数组实现
日拱算法:解两道“杨辉三角”题
思路简单,把握杨辉三角特点:第0行1个元素,第1行2个元素,第2行3个元素;依此例推
掘金安东尼
2022/09/19
1900
日拱算法:解两道“杨辉三角”题
【每日一题】4.LeetCode——杨辉三角
杨辉三角的第0行只有一个数:1。对于 1 ≤ i < numRows。用pervRow表示杨辉三角的第 i - 1行,用curRow表示杨辉三角的第 i 行.
爱敲代码的小杨.
2024/05/07
1270
【每日一题】4.LeetCode——杨辉三角
LeetCode每日一练(杨辉三角)
题目要求的是给定一个非负索引k,要求得到杨辉三角中的第k行,杨辉三角相信大家都不陌生了吧,不明白的同学去百度一下补补课呦。
wangweijun
2022/01/10
5810
LeetCode每日一练(杨辉三角)
leetcode ​# 118:Pascal's Triangle 杨辉三角
Given a non-negative integer numRows, generate the first numRows of Pascal's triangle.
爱写bug
2019/06/25
3700
leetcode ​# 118:Pascal's Triangle 杨辉三角
Leetcode#118. Pascal's Triangle(杨辉三角)
题目描述 给定一个非负整数 numRows,生成杨辉三角的前 numRows 行。 在杨辉三角中,每个数是它左上方和右上方的数的和。 示例: 输入: 5 输出: [ [1], [1,
武培轩
2018/09/28
5800
Leetcode#118. Pascal's Triangle(杨辉三角)
☆打卡算法☆LeetCode 119. 杨辉三角 II 算法解析
“给定一个非负索引 rowIndex ,返回 杨辉三角的第 rowIndex 行。”
恬静的小魔龙
2022/08/07
2040
☆打卡算法☆LeetCode 119. 杨辉三角 II 算法解析
leetcode188.杨辉三角[easy](python)
首先,由于杨辉三角最外层的两边都是1,且杨辉三角的计算需要根据这两边计算,所以我直接初始化一个二维数组,第一维表示有几行,第二维度表示每行有几个元素,初始化值为1,然后我们观察发现,假设参与运算的节点坐标为i,j,则新的杨辉三角的值为arr[i+1][j] = arr[i][j-1] + arr[i][j]。
从不摸鱼的van
2023/10/19
2170
【小Y学算法】⚡️每日LeetCode打卡⚡️——33.杨辉三角
根据题目中给出的图形示例,我们需要定义一个 jagged(锯齿)数组,它的长度与 numRows 一样。
呆呆敲代码的小Y
2021/09/23
2520
【小Y学算法】⚡️每日LeetCode打卡⚡️——33.杨辉三角
相关推荐
​LeetCode刷题实战119: 杨辉三角 II
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档