Mockito

小王学习录

  • 依赖
  • 注解
    • Mock
    • @Spy
    • 静态方法单元测试
    • @InjectMocks 注解
    • @Captor 注解
    • @BeforeAll 和 BeforeEach的区别
    • @ParameterizedTest
      • @ValueSource
      • @EnumSource
      • @CsvSource
      • @MethodSource
  • 打桩
    • 打桩方式
    • 打桩参数匹配方式

在这里插入图片描述

依赖

       <!-- 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>

        <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.10.2</version>
            <scope>test</scope>
        </dependency>

注解

Mock

class MockTestTest {
    @Mock
    private MockTest demo;
    @BeforeEach
    void setup(){
        MockitoAnnotations.openMocks(this);
    }
    @Test
    void add() {
        Mockito.when(demo.add(1, 2)).thenReturn(3);
        System.out.println(demo.add(1, 2));
        Assertions.assertEquals(3, demo.add(1, 2));
        Mockito.verify(demo, Mockito.times(2)).add(1, 2);
    }
    @AfterEach
    void after(){
        System.out.println("测试完毕 ");
    }
}

@Spy

class SpyTestTest {
    @Spy
    private SpyTest spyTest;
    @BeforeEach
    void setUp(){
        MockitoAnnotations.openMocks(this);
    }
    @Test
    void add() {
        Mockito.when(spyTest.add(1, 2)).thenReturn(4);
        System.out.println(spyTest.add(1, 2));
        Mockito.verify(spyTest).add(1,2);
        Assertions.assertEquals(4, spyTest.add(1, 2));
        //Mockito.verify(spyTest, Mockito.times(2)).add(1, 2);
        Mockito.when(spyTest.add(1, 2)).thenCallRealMethod();
        System.out.println(spyTest.add(1, 2));
    }
}

静态方法单元测试

class StaticTestTest {
    @Test
    void add() {
        MockedStatic<StaticTest> test1 = Mockito.mockStatic(StaticTest.class);
        test1.when(()->StaticTest.add(1, 2)).thenReturn(4);
        System.out.println(StaticTest.add(1, 2));
        Assertions.assertEquals(4, StaticTest.add(1, 2));
    }
}

@InjectMocks 注解

@Captor 注解

@Captor 是 Mockito 框架中的一个注解,用于创建 ArgumentCaptor 实例。ArgumentCaptor 在单元测试中扮演着重要角色,它允许你在测试过程中捕获(capture)被测方法调用时传入的实际参数值,以便对这些参数进行详细的断言验证。

假设有一个 EmailService 类,它有一个方法 sendEmail(Recipient recipient, EmailMessage message),我们想要测试这个方法是否正确地将邮件发送给了预期的收件人。可以编写如下使用 @Captor 注解的单元测试:

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import static org.mockito.Mockito.verify;

public class EmailServiceTest {

    @Mock
    private EmailGateway emailGateway; // Mocked dependency for sending emails

    private EmailService emailService;

    @Captor
    private ArgumentCaptor<Recipient> recipientCaptor;

    @Captor
    private ArgumentCaptor<EmailMessage> messageCaptor;

    @BeforeEach
    void setUp() {
        MockitoAnnotations.initMocks(this);
        emailService = new EmailService(emailGateway);
    }

    @Test
    public void testSendEmail() {
        Recipient expectedRecipient = new Recipient("john.doe@example.com");
        EmailMessage expectedMessage = new EmailMessage("Subject", "Body");

        emailService.sendEmail(expectedRecipient, expectedMessage);

        // 使用 captors 捕获实际传递给 sendEmail 方法的参数
        verify(emailGateway).sendEmail(recipientCaptor.capture(), messageCaptor.capture());

        // 使用 captured 参数进行断言
        Recipient actualRecipient = recipientCaptor.getValue();
        EmailMessage actualMessage = messageCaptor.getValue();

        assertEquals(expectedRecipient.getEmail(), actualRecipient.getEmail());
        assertEquals(expectedMessage.getSubject(), actualMessage.getSubject());
        assertEquals(expectedMessage.getBody(), actualMessage.getBody());
    }
}
  1. 使用 @Mock 注解创建了 EmailGateway 的 mock 对象。
  2. 使用 @Captor 注解声明了两个 ArgumentCaptor 实例,分别对应 sendEmail 方法的 Recipient 和 EmailMessage 参数。
  3. 在 setUp 方法中初始化 mock 和被测试的 EmailService。
  4. 在 testSendEmail 测试方法中,调用 emailService.sendEmail() 方法,传入期望的参数。
  5. 使用 verify 方法验证 emailGateway.sendEmail() 是否被调用,并且使用 captors 捕获实际传递的参数。
  6. 最后,通过 captors 获取捕获的参数值,并进行详细的断言验证,确保它们与预期值相匹配。
  7. 通过 @Captor 注解和 ArgumentCaptor,我们能够有效地验证 sendEmail 方法是否正确地将预期的收件人和邮件内容传递给了依赖的 EmailGateway。

@BeforeAll 和 BeforeEach的区别

@BeforeAll 和 @BeforeEach 是 JUnit 5 中用于在测试执行前进行初始化工作的两个注解,它们的主要区别在于执行时机和适用场景:

@BeforeAll

执行时机:
@BeforeAll 注解的方法将在当前测试类中所有测试方法执行之前只执行一次。

适用场景:

  • 一次性昂贵资源的初始化:当测试类中的所有测试方法都需要共享一个昂贵的资源(如数据库连接、网络连接、第三方服务客户端等),并且创建或连接这个资源的成本较高时,使用 @BeforeAll 来初始化一次,避免在每个测试方法前重复执行。
  • 全局数据加载:如果有一组测试数据需要从文件、数据库或其他外部源加载,且所有测试方法都会使用同一份数据,那么可以使用 @BeforeAll 来提前加载数据,避免多次加载带来的性能开销。
  • 长时间运行的预设条件:当有一些耗时较长但对所有测试方法通用的预设条件(如模拟复杂环境、配置全局系统状态等),应放在 @BeforeAll 注解的方法中执行。
  • 静态环境配置:如果需要对静态变量、系统属性或全局配置进行一次性设定,适用于 @BeforeAll。
    注意事项:

@BeforeAll 注解的方法必须是静态方法,因为它们在测试类实例化之前执行。
如果这些方法抛出异常,所有测试方法都将跳过执行。

@BeforeEach
执行时机:
@BeforeEach 注解的方法将在每个测试方法执行之前单独执行一次。

适用场景:
测试数据的独立准备:每个测试方法可能需要不同的测试数据或对象状态,使用 @BeforeEach 可以为每个测试方法单独准备所需的测试数据或对象实例。
对象重置:如果被测试对象具有状态,且每次测试方法执行后需要将其状态重置为初始状态,@BeforeEach 方法可以负责这项清理和重置工作。
局部依赖注入:对于那些仅在一个测试方法执行期间有效的依赖项,可以在 @BeforeEach 方法中注入。
重复性环境配置:对于每次测试开始时都需要重新配置的环境设置(如清除缓存、重置模拟对象状态等),应在 @BeforeEach 注解的方法中进行。
注意事项:

@BeforeEach 注解的方法不能是静态的,因为它们与每个测试方法的执行紧密相关,通常需要访问非静态的成员变量或方法。
如果某个 @BeforeEach 方法执行失败(抛出异常),仅影响紧随其后的那个测试方法,其他测试方法仍会尝试执行各自的 @BeforeEach 方法。

总结来说,@BeforeAll 用于执行一次即可满足整个测试类需求的、成本较高的初始化操作,适用于共享资源的设置和全局数据的加载;而 @BeforeEach 则是在每个测试方法执行前进行针对性的、可能因测试方法不同而有所差异的准备工作,确保每个测试方法在独立、一致的环境中运行。选择使用哪一个注解取决于测试场景的具体需求和资源管理策略。

@ParameterizedTest

@ParameterizedTest 是 JUnit 5 提供的一种用于执行参数化测试的注解。参数化测试允许同一个测试方法使用不同的输入数据集(参数)多次执行,从而提高测试覆盖率和代码复用性。通过这种方式,可以轻松地验证被测代码在多种不同情况下的行为,确保其正确性和一致性。

通常需要配合@ValueSource、@EnumSource、@CsvSource、@MethodSource一起使用。

@ValueSource

用途:@ValueSource 用于为参数化测试提供一组固定的值。这些值可以是基本类型(如 int、double、String 等)或其对应的包装类型。

使用场景:
测试某个方法或函数在一系列特定值上的表现,如验证数学函数在不同整数、浮点数上的计算结果。
验证类的构造函数或方法对各种边界值(如最大值、最小值、零值、正负值等)的处理是否正确。

@ParameterizedTest
@ValueSource(ints = {1, 2, 3, 4, 5})
void testSquareRoot(int input, int expectedOutput) {
    double result = Math.sqrt(input);
    assertEquals(expectedOutput, result, 0.01, "Square root of " + input + " should be close to " + expectedOutput);
}

@EnumSource

用途:@EnumSource 专门用于为参数化测试提供枚举类型的值。它可以指定要使用的枚举类,并可以选择性地提供筛选条件(如包含特定名称或值的枚举常量)。

使用场景:
对枚举类型的类进行全面测试,确保其每个枚举常量在特定场景下的行为正确。
当测试方法需要验证针对枚举类型的逻辑时,如序列化、反序列化、字符串转换等。

enum Color { RED, GREEN, BLUE }

@ParameterizedTest
@EnumSource(Color.class)
void testColorToString(Color color, String expectedString) {
    String result = color.toString();
    assertEquals(expectedString, result, "toString() should return the correct string for " + color);
}

@CsvSource

用途:@CsvSource 用于提供以逗号分隔值(CSV)格式的数据集。每一行代表一组参数,各参数间以逗号分隔。这对于多参数的测试方法非常方便,可以直接在注解中列出多行数据。

使用场景:
当测试需要多组不同参数组合时,如验证数学运算、字符串处理函数、日期格式转换等。
当测试数据以表格形式存储或展示时,可以直接复制粘贴成 CSV 格式。

@ParameterizedTest
@CsvSource({
    "1, 2, 3",
    "10, 5, 15",
    "-1, .jpg, .png",
    "abc, def, abcdef"
})
void testConcatenate(String part1, String part2, String expectedConcatenation) {
    String result = concatenate(part1, part2);
    assertEquals(expectedConcatenation, result, "Concatenation of " + part1 + " and " + part2 + " should be " + expectedConcatenation);
}

@MethodSource

用途:@MethodSource 允许参数化测试的数据来源于测试类中的一个静态方法。这个方法返回一个 Stream,其中每个 Arguments 对象封装了一组参数。这种方法提供了最大的灵活性,可以生成动态数据、读取外部资源、甚至执行复杂的逻辑来提供测试参数。

使用场景:
数据集较大或需要动态生成时,可以编写一个方法来按需生成或读取数据。
测试需要依赖外部配置文件、数据库查询结果、随机数据生成器等复杂数据源。
需要根据测试环境动态调整测试参数。

static Stream<Arguments> additionTestData() {
    return Stream.of(
            Arguments.of(1, 2, 3),
            Arguments.of(-1, 3, 2),
            Arguments.of(0, 0, 0),
            Arguments.of(100, -50, 50),
            Arguments.of(1.5, 2.75, 4.25)
    );
}

 @ParameterizedTest
@MethodSource("additionTestData")
void testAdd(double num1, double num2, double expectedSum) {
    double result = calculator.add(num1, num2);
    assertEquals(expectedSum, result, "The sum should be correct for inputs: " + num1 + ", " + num2);
}

总结起来,@ValueSource、@EnumSource、@CsvSource 和 @MethodSource 分别提供了不同类型和来源的参数数据集

打桩

打桩方式

1、when().thenReturn()

2、doReturn().when()

以上两种是有返回值的方法打桩,通常使用。

3、doNothing().when()…

void方法打桩;

4、when().thenThrow()

5、doThrow().when()

以上两种是抛异常打桩;

6、when().thenAnswer()

7、doAnswer().when()

以上两种是复杂参数打桩,可以根据传参自定义返回结果。

8、@InjectMocks+@Spy,打桩被测类的某个方法

正常被单测对象使用@InjectMocks注解,被测业务逻辑复杂,比如被测方法A()→B()→C()。如果当前单元测试用例不打算测试到C()方法,就需要结合@Spy和具体打桩方式跳过C()方法。

打桩参数匹配方式

打桩时,如果被测方法有输入参数,有以下方式传参:

any()、anyList()、anyByte()、anyBoolean()、anyChar()、anyCollection()、anyDouble()、anyFloat()、anyInt()、anyIterable()、anyLong()、anyMap()、anySet()、anyShort()、anyString()

通常仅需要any(),如果被打桩的测试方法有重载,则需要指定具体的参数类型。

这些传参方法是Mockito框架中用于创建“任意匹配器”的工具,它们在编写单元测试时非常有用,特别是当我们要验证方法被调用时的参数类型和数量,而不关心具体的参数值时。简单举几个例子

  1. any():

用途:匹配任何类型的对象。当被测方法接受的对象类型不确定或不关心其具体值时使用。

import static org.mockito.Mockito.*;

class Service {
    void process(Object input) {
        // ...
    }
}

@Test
void testProcessMethod() {
    Service service = mock(Service.class);
    service.process(any()); // 匹配任何类型的对象作为输入参数

    verify(service).process(any()); // 验证process方法被调用时,传入了任意对象
}
  1. anyList():

用途:匹配任何类型的列表(实现 List 接口的对象)。当被测方法接受一个列表作为参数,但不关心列表的具体元素时使用。

import static org.mockito.Mockito.*;

class Service {
    void handleList(List<String> items) {
        // ...
    }
}

@Test
void testHandleListMethod() {
    Service service = mock(Service.class);
    service.handleList(anyList()); // 匹配任何类型的列表作为输入参数

    verify(service).handleList(anyList()); // 验证handleList方法被调用时,传入了任意列表
}
  1. anyCollection():

用途:匹配任何类型的 Collection 对象(实现了 Collection 接口的对象)。当被测方法接受一个集合作为参数,但不关心集合的具体元素时使用。

import static org.mockito.Mockito.*;

class Service {
    void handleCollection(Collection<String> items) {
        // ...
    }
}

@Test
void testHandleCollectionMethod() {
    Service service = mock(Service.class);
    service.handleCollection(anyCollection()); // 匹配任何类型的 Collection 对象作为输入参数

    verify(service).handleCollection(anyCollection()); // 验证handleCollection方法被调用时,传入了任意 Collection 对象
}
  1. anyIterable():

用途:匹配任何实现了 Iterable 接口的对象。当被测方法接受一个可迭代对象作为参数,但不关心其具体元素时使用。

import static org.mockito.Mockito.*;

class Service {
    void processIterable(Iterable<String> items) {
        // ...
    }
}

@Test
void testProcessIterableMethod() {
    Service service = mock(Service.class);
    service.processIterable(anyIterable()); // 匹配任何 Iterable 对象作为输入参数

    verify(service).processIterable(anyIterable()); // 验证processIterable方法被调用时,传入了任意 Iterable 对象
}
  1. anyMap():

用途:匹配任何类型的 Map 对象(实现了 Map 接口的对象)。当被测方法接受一个映射作为参数,但不关心其具体键值对时使用。

import static org.mockito.Mockito.*;

class Service {
    void updateData(Map<String, Integer> data) {
        // ...
    }
}

@Test
void testUpdateDataMethod() {
    Service service = mock(Service.class);
    service.updateData(anyMap()); // 匹配任何 Map 对象作为输入参数

    verify(service).updateData(anyMap()); // 验证updateData方法被调用时,传入了任意 Map 对象
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/565963.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Django模型的属性与方法

本节介绍Django模型的属性和方法&#xff0c;以及如何重写之前定义的模型方法等内容。 3.5.1 模型属性 Django模型中最重要的属性就是Manager&#xff0c;它是Django模型和数据库查询操作之间的接口&#xff0c;并且被用作从数据库当中获取实例的途径。如果Django模型中没有…

探索大型语言模型(LLM)在人类性格个性评估(MBTI)中的前景与应用

1.概述 大型语言模型&#xff08;LLM&#xff09;如ChatGPT在各个领域的应用确实越来越广泛&#xff0c;它们利用庞大的数据集进行训练&#xff0c;以模拟人类的语言理解和生成能力。这些模型在提供信息、解答问题、辅助决策等方面表现出了强大的能力&#xff0c;但它们并不具…

微电子领域常见概念(六)化学键合

微电子领域常见概念&#xff08;六&#xff09;化学键合 化学键合是化学中一个非常基础且重要的概念&#xff0c;它描述了原子之间通过电子的相互作用形成的连接。可以进行以下分类&#xff1a; 1. 离子键合&#xff08;Ionic Bonding&#xff09; • 定义&#xff1a;离子键合…

如何查看自己的公网IP?

我们在网络中&#xff0c;每一个设备都被分配了一个唯一的IP地址&#xff0c;用以区分和识别其他设备。公网IP地址是指可被公众访问的IP&#xff0c;是因特网上的全球唯一标识。当我们需要查看自己的公网IP时&#xff0c;可以采取以下几种方式。 使用命令行查看公网IP 在Windo…

书生·浦语大模型实战训练营--第二期第七节--OpenCompass大模型评测实战--homework

一、配置环境 安装下面的顺序以及自己的文件路径配置环境 conda create -n opencompass python3.10 -y 安装下面的包 absl-py accelerate>0.19.0 boto3 cn2an cpm_kernels datasets>2.12.0 einops0.5.0 evaluate>0.3.0 fairscale func_timeout fuzzywuzzy immutab…

Linux 系统IO函数之stat、lstat函数

1、stat函数 要点&#xff1a; int stat(const char *pathname, struct stat *statbuf); 作用&#xff1a;查看文件的信息 man 2 stat/return value1、stat结构体&#xff1a; 2、sturct stat 结构体中 st_mode 的含义&#xff08;文件的类型和存取的权限&#xff09;: st_mo…

zabbix“专家坐诊”第237期问答

问题一 Q&#xff1a;在一台虚拟机安装了mysql数据库服务器上安装了agent&#xff0c;将MySQL by Zabbix agent模板联接上去了&#xff0c;但增加的mysql监控项&#xff0c;全部显示为不支持的&#xff0c;这是什么原因&#xff1f; A&#xff1a;这个是自定义脚本的形式&#…

IBM SPSS Statistics for Mac:强大的数据分析软件

IBM SPSS Statistics for Mac是一款功能强大的数据分析软件&#xff0c;专为Mac用户设计&#xff0c;提供了一系列专业的统计分析和数据管理功能。无论是科研人员、数据分析师还是学生&#xff0c;都能从中获得高效、准确的数据分析支持。 IBM SPSS Statistics for Mac v27.0.1…

【UI】element-ui的el-dialog的遮罩层在模态框的前面bug

最近在写element ui 的时候使用dialog组件&#xff0c;偶然出现了这种情况 原因&#xff1a; 是因为遮罩层插入进了body标签下&#xff0c;z-index高于当前父元素。 解决&#xff1a;在el-dialog标签里加上:modal-append-to-body"false"就可以了。 饿了么官网文档&a…

熊猫电竞赏金赛系统源码 APP+H5双端源码附搭建教程下载

熊猫电竞赏金系统简介 熊猫电竞赏金电竞系统 赏金赛源码&#xff0c;用户通过平台打比赛&#xff0c;赢了获得奖金奖励&#xff0c; 金币赛、赏金赛、vip赛等种赛事 可开王者荣耀、和平精英比赛 支持1v1、单排、双排组、战队排等多种比赛模式 支持QQ区、微信区 游戏玩的好…

大语言模型(LLM)漏洞爆发,AI模型无一幸免

本文概述了人工智能初创公司Anthropic于2024年04月03日发表的一篇针对人工智能安全的论文&#xff0c;该公司在本论文中宣布的一种新的“越狱”技术&#xff0c;名为Many-shot Jailbreaking&#xff08;多轮越狱&#xff09;。文章详细描述了目前大语言模型&#xff08;LLM&…

iOS 在OC旧项目中使用Swift进行混编

iOS 在OC旧项目中使用Swift进行混编 1、创建桥接文件 ​ 第一次在Swift创建OC文件&#xff0c;或者第一次OC创建Swift时&#xff0c;xcode会提示桥接&#xff0c;Creat Bridging Header即可,这个文件用于Swift调用OC文件&#xff0c;与OC调用Swift无关。 2、在TARGETS中设置D…

深入docker-swarm overlay网络模型

目录 1.简介 2.网络模型 3.docker_gwbridge网络 3.1.docker_gwbridge网关地址 3.2.检查docker_gwbridge网络 3.2.1.查找任务容器eth接口 3.2.2.查找ingress-sbox容器eth接口 4.检查ingress网络 4.1.检查ingress网络 4.2.检查ingress网络的命名空间 4.2.1.查找任务容…

LLM学习之自然语言处理简单叙述

自然语言处理基础 自然语言处理&#xff1a;让计算机读懂人所写好的这些文本&#xff0c;能够像人一样进行交互。 自然语言处理的任务和应用 任务&#xff1a; 词性标注 part of speech tagging 动词&#xff0c;名词&#xff0c;形容词&#xff1f; 命名实体的识别 name…

iOS OC项目中引入SwiftUI文件

iOS OC项目中引入SwiftUI文件 1、创建SwiftUI文件 2、第一次创建时&#xff0c;Xcode会提示桥接&#xff0c;选择 Creat Bridging Header即可。 3、创建swift管理类 /**在UIKit中使用SwiftUI&#xff0c;需要使用UIHostingController对SwiftUI进行包装&#xff0c;返回的是U…

uniapp自定义顶部导航栏

首先uniapp获取设备信息&#xff1a;uni.getSystemInfo或uni.getSystemInfoSync&#xff0c;可用于设置顶部安全区 留一个设备安全区的位置哦 然后在pages.json文件里配置自定义导航栏 {"pages": [ //pages数组中第一项表示应用启动页&#xff0c;参考&#xff1a…

用友U8-Cloud api/hr接口存在SQL注入漏洞

声明&#xff1a; 本文仅用于技术交流&#xff0c;请勿用于非法用途 由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;文章作者不为此承担任何责任。 简介 U8 Cloud是由用友推出的新一代云ERP系统&#xff0…

【PhpStorm的环境配置与应用的简单介绍】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

国内开通chatgpt plus会员方法

ChatGPT镜像 今天在知乎看到一个问题&#xff1a;“平民不参与内测的话没有账号还有机会使用ChatGPT吗&#xff1f;” 从去年GPT大火到现在&#xff0c;关于GPT的消息铺天盖地&#xff0c;真要有心想要去用&#xff0c;途径很多&#xff0c;别的不说&#xff0c;国内GPT的镜像…

服务器如何开启远程连接?

服务器开启远程连接是网络管理中一项重要的功能。通过远程连接&#xff0c;用户可以在任何地方远程访问服务器&#xff0c;从而进行管理、维护和监控等操作。远程连接的开启可以为工作提供便利性和效率&#xff0c;但同时也带来了安全风险。确保远程连接的安全性和可靠性是至关…