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注解修饰的对象里面

相关推荐
有梦想的攻城狮11 分钟前
maven中的maven-resources-plugin插件详解
java·maven·插件·maven插件·maven-resources
异常君12 分钟前
深入理解 Java 类卸载:避免 90% 的内存泄漏问题
java·代码规范
Quke陆吾13 分钟前
Maven概述,搭建,使用
java·maven
不似桂花酒14 分钟前
打开一个新的Maven工程要做的事情
java·maven·intellij-idea
芦屋花绘27 分钟前
方法重写与方法重载详解
java·spring boot
zh_xuan29 分钟前
java Semaphore‌
java·开发语言
残*影1 小时前
什么是 CPU 缓存模型?
java·缓存
Musennn1 小时前
leetcode93.复原IP地址:回溯算法中段控制与前导零处理的深度解析
java·数据结构·算法·leetcode
用户0595661192091 小时前
现代应用中 Java 集合框架的核心技术与实践解析
java·后端
t198751282 小时前
Java连接Redis和基础操作命令
java·开发语言·redis