Java测试框架Mockito快速入门

Mockito结合TestNG快速入门

什么是Mockito

Mockito 是一个专门用于 Java 的强大测试框架,主要用来创建和管理模拟对象,辅助开发者进行单元测试,具有以下特点和功能:

  • 创建模拟对象:能通过简洁的语法创建类或接口的模拟版本,这些模拟对象可作为真实对象的替代品,在调试期间使用,帮助隔离外部依赖。比如在测试一个依赖其他服务(如账户服务、数据库访问服务等)的业务逻辑时,可模拟这些外部服务,避免在测试中涉及真实的复杂操作(如真的修改数据库数据、调用远程接口等) 。
  • 定义行为 :可以对模拟对象的方法进行 "桩(Stubbing)" 设置,即指定其返回值或让其抛出异常。例如,使用when(...).thenReturn(...)来定义方法返回特定值;用thenThrow()模拟方法抛出异常,方便测试错误处理逻辑。
  • 验证交互 :提供验证机制,通过verify(...)方法检查模拟对象的方法是否按照预期被调用,以及调用的次数,从而验证代码与模拟对象之间的交互是否正确

引入的依赖

mockito-core

复制代码
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>5.15.2</version>
    <scope>test</scope>
</dependency>

testng

复制代码
        <!-- https://mvnrepository.com/artifact/org.testng/testng -->
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>7.11.0</version>
            <scope>test</scope>
        </dependency>

为什么需要mock

可以知道这个方法的调用情况,调用了多少次参数就是多少

给这个对象的行为做一个定义,来指定返回结果或者指定特定的动作

当使用mock对象时,如果不对其行为进行定义,则mock对象方法的返回值为返回类型的默认值


验证和断言

验证:vertify()方法

验证是校验待验证的对象是否发生过某些行为

复制代码
package com.example.testng;

import org.mockito.Mockito;
import org.testng.annotations.Test;

import java.util.Random;

public class MockitoDemoTest {
    @Test
    public void test(){
        //mock一个对象
       Random mock= Mockito.mock(Random.class);
       //输出随机数
       System.out.println(mock.nextInt());
        //验证我们的mock对象是否用了nextInt()这个方法
        Mockito.verify(mock).nextInt();


    }
}

为什么我们的执行结果一直是0?

这是因为我们没有给我们的mock出来的对象定义行为

断言:Assert

判断我们返回的结果是否符合我们的预期

复制代码
package com.example.testng;

import org.mockito.Mockito;
import org.testng.Assert;
import org.testng.annotations.Test;

import java.util.Random;

public class MockitoDemoTest {
    @Test
    public void test() {
        //mock一个对象
        Random mock = Mockito.mock(Random.class);
        Mockito.when(mock.nextInt()).thenReturn(100);

        Assert.assertEquals(100, mock.nextInt());


    }
}

符合预期,我们成功通过

不符合预期,我们测试失败

复制代码
package com.example.testng;

import org.mockito.Mockito;
import org.testng.Assert;
import org.testng.annotations.Test;

import java.util.Random;

public class MockitoDemoTest {
    @Test
    public void test() {
        //mock一个对象
        Random mock = Mockito.mock(Random.class);
        Mockito.when(mock.nextInt()).thenReturn(100);

        Assert.assertEquals(101, mock.nextInt());


    }
}

给Mock对象进行打桩

打桩的意思其实就是对mock对象的行为进行定义

打桩的话是mockito里面的when()方法,定义我们的行为


@Mock注解

我们的mock注解必须搭配MockitoAnnotations.openMocks(testClass)方法一起使用

我们使用@Mock注解来Mock对象,而不是在代码里面手动Mock我们的对象

复制代码
package com.example.testng;

import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.testng.Assert;
import org.testng.annotations.Test;

import java.util.Random;

public class MockitoDemoTest {


    @Mock
    private Random mock;
    @Test
    public void test() {
        MockitoAnnotations.openMocks(this);

        Mockito.when(mock.nextInt()).thenReturn(100);

        Assert.assertEquals(100, mock.nextInt());


    }
}

@BeforeTets与@AfterTest

复制代码
package com.example.testng;


import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.testng.Assert;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

import java.util.Random;

public class MockitoDemoTest {


    @Mock
    private Random mock;

    @BeforeTest
    void setup() {
        System.out.println("测试前的准备");
    }

    @AfterTest
    void end() {
        System.out.println("测试结束");
    }

    @Test
    public void test() {
        MockitoAnnotations.openMocks(this);

        Mockito.when(mock.nextInt()).thenReturn(100);

        Assert.assertEquals(100, mock.nextInt());


    }
}

Spy方法与@Spy注解

spy方法与mock方法的不同是

1.被spy的对象会走真实的方法而mock对象不会

2.spy{}方法的参数是对象实例,mock的参数是class

复制代码
package com.example.testng;


import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.testng.Assert;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

import java.util.Random;

public class MockitoDemoTest {


    @Spy
    private Random mock;

    @BeforeTest
    void setup() {
        MockitoAnnotations.openMocks(this);
    }


    @Test
    public void test() {


        Assert.assertEquals(100, mock.nextInt());


    }
}

我们没有打桩,而且我们是Spy注解,所以我们不会输出0,而是会去调用真正的Random方法

如果我们打桩了的话,那还是会按照我们打桩定义的规则来


打桩与Mock静态方法

4个常见的打桩方法

thenCallRealMethod

thenReturn

thenThrow

thenAnswer

复制代码
package com.example.testng;


import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.testng.Assert;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

import java.util.Random;

import static org.mockito.Mockito.when;

public class MockitoDemoTest {


    @Spy
    private Random mock;

    @BeforeTest
    void setup() {
        MockitoAnnotations.openMocks(this);
    }


    @Test
    public void test() {


        //调用真实方法
        when(mock.nextInt()).thenCallRealMethod();
        //定义返回值
        when(mock.nextInt()).thenReturn(1);
        //定义返回什么错误
        when(mock.nextInt()).thenThrow(new RuntimeException());
        //自定义响应逻辑
        when(mock.nextInt()).thenAnswer(new Answer<Integer>() {
            @Override
            public Integer answer(InvocationOnMock invocation) throws Throwable {
                // 这里可以编写自定义的逻辑
                // invocation 包含了方法调用的信息,例如方法名、参数等
                // 这里简单返回一个固定值 42
                return 42;
            }
        });

    }
}

Mock静态方法

依赖

mockito-inline依赖包含了mockito-code依赖

注意我们使用mockito-inline依赖的时候要把mockito-code依赖注释掉,我们不能同时引用

复制代码
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-inline -->
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-inline</artifactId>
    <version>5.2.0</version>
    <scope>test</scope>
</dependency>

在软件开发的单元测试中,有时候我们需要对静态方法进行模拟(Mock),这样可以更好地控制测试环境、隔离依赖,提高测试的可维护性和稳定性。下面详细介绍模拟静态方法的相关内容,以 Java 语言结合 Mockito 框架为例


为什么需要 Mock 静态方法

隔离依赖:静态方法可能依赖于外部资源,如文件系统、数据库等。在单元测试中,我们希望避免这些外部依赖的影响,通过模拟静态方法可以将测试与外部资源隔离开来

控制返回值:静态方法的返回值可能受到多种因素的影响,通过模拟静态方法,我们可以精确控制其返回值,从而更方便地验证业务逻辑


使用例子

我们这个类里面有个静态方法

复制代码
public class StaticUtils {
    public static int add(int a, int b) {
        return a + b;
    }
}

使用mockStatic

使用 try-with-resources语句确保 MockedStatic 对象在使用完毕后自动关闭,避免对后续测试产生影响

复制代码
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mockStatic;

public class StaticUtilsTest {

    @Test
    public void testMockStaticMethod() {
        // 使用 try-with-resources 语句创建 MockedStatic 对象
        try (MockedStatic<StaticUtils> mockedStatic = mockStatic(StaticUtils.class)) {
            // 定义静态方法的行为
            mockedStatic.when(() -> StaticUtils.add(2, 3)).thenReturn(10);

            // 调用静态方法
            int result = StaticUtils.add(2, 3);

            // 验证结果
            assertEquals(10, result);
        }
    }
}

单元测试如何提高我们的代码覆盖率

@InjectMocks注解

剩下的@mock和@spy注解修饰的对象会自动注入到被InjectMocks注解修饰的对象里面

相关推荐
暮乘白帝过重山10 分钟前
Singleton和Prototype的作用域与饿汉式/懒汉式的初始化方式
spring·原型模式·prototype·饿汉式·singleton·懒汉式
腥臭腐朽的日子熠熠生辉39 分钟前
解决maven失效问题(现象:maven中只有jdk的工具包,没有springboot的包)
java·spring boot·maven
ejinxian40 分钟前
Spring AI Alibaba 快速开发生成式 Java AI 应用
java·人工智能·spring
杉之1 小时前
SpringBlade 数据库字段的自动填充
java·笔记·学习·spring·tomcat
圈圈编码1 小时前
Spring Task 定时任务
java·前端·spring
俏布斯1 小时前
算法日常记录
java·算法·leetcode
27669582921 小时前
美团民宿 mtgsig 小程序 mtgsig1.2 分析
java·python·小程序·美团·mtgsig·mtgsig1.2·美团民宿
爱的叹息1 小时前
Java 连接 Redis 的驱动(Jedis、Lettuce、Redisson、Spring Data Redis)分类及对比
java·redis·spring
程序猿chen2 小时前
《JVM考古现场(十五):熵火燎原——从量子递归到热寂晶壁的代码涅槃》
java·jvm·git·后端·java-ee·区块链·量子计算
niuniu_6662 小时前
Selenium 性能测试指南
selenium·测试工具·单元测试·测试·安全性测试