JUnit 5和Mockito进行单元测试!

1. JUnit 5 基础

  JUnit 5是最新的JUnit版本,它引入了许多新特性,包括更灵活的测试实例生命周期、参数化测试、更丰富的断言和假设等。

  1.1 基本注解

  @Test:标记一个方法为测试方法。

  @BeforeEach:在每个测试方法之前执行。

  @AfterEach:在每个测试方法之后执行。

  @BeforeAll:在所有测试方法之前执行一次(必须是静态方法)。

  @AfterAll:在所有测试方法之后执行一次(必须是静态方法)。

  @DisplayName:定义测试类或测试方法的自定义名称。

  @Nested:允许将测试类分组到更小的测试类中。

  @ParameterizedTest:进行参数化测试。

  1.2 断言(Assertions类)

  Assertions 类是JUnit 5中用于断言的核心类,提供了一系列静态方法来验证测试条件是否满足预期。

  assertEquals(expected, actual):验证两个对象是否相等。如果不等,测试失败。

  assertTrue(boolean condition):验证条件是否为真。如果为假,测试失败。

  assertFalse(boolean condition):验证条件是否为假。如果为真,测试失败。

  assertNull(Object object):验证对象是否为null。如果不为null,测试失败。

  assertNotNull(Object object):验证对象是否不为null。如果为null,测试失败。

  assertThrows(Class<T> expectedType, Executable executable):验证执行executable是否抛出了expectedType类型的异常。如果没有抛出或抛出其他类型的异常,测试失败。

  assertAll(Executable... executables):同时执行多个断言,如果所有断言都成功,则测试通过;如果任何一个断言失败,所有失败的断言都会被报告。

  assertSame(expected, actual):验证两个对象是否为同一个对象(使用==比较)。如果不是,测试失败。

  assertNotSame(unexpected, actual):验证两个对象是否不是同一个对象(使用==比较)。如果是,测试失败。

  assertTimeout(Duration timeout, Executable executable):验证执行executable是否在给定的时间内完成。如果执行超时,测试失败。

  1.3  Assumptions 类

  Assumptions 类提供了基于某些条件判断是否执行测试的能力。如果假设失败(即条件不满足),当前测试会被跳过,而不是失败。以下是一些常用的Assumptions方法:

  assumeTrue(boolean assumption):如果假设为真,则继续执行测试;如果假设为假,测试被跳过。

  assumeFalse(boolean assumption):如果假设为假,则继续执行测试;如果假设为真,测试被跳过。

  assumingThat(boolean assumption, Executable executable):如果假设为真,则执行给定的executable(可以是一个测试方法);无论假设结果如何,测试都会继续执行,但executable只在假设为真时执行。

  2.Mockito 基础

  Mockito是一个流行的Java mocking框架,用于在隔离环境中测试代码,通过模拟依赖来确保测试的独立性。

  基本注解:

  @Mock:创建一个模拟对象。

  @InjectMocks:创建一个实例,其字段或构造器依赖将被@Mock注解的模拟对象自动注入。

  @Spy:可以创建一个真实的对象,并在需要时对它的某些方法进行模拟。

  @Captor:用于捕获方法调用的参数。

  常用方法:

  mock(Class<T> classToMock):创建一个类的模拟对象。这是创建模拟对象的基础。

  when(T methodCall):当你想模拟一个方法调用的返回值时使用。与thenReturn一起使用,可以指定一个方法调用应该返回什么值。

  thenReturn(T value):与when方法一起使用,用于指定方法调用的返回值。

  doReturn(Object toBeReturned):一个替代thenReturn的方法,用在当你需要模拟void方法或在spy对象上进行模拟时。

  verify(T mock):用于验证某个模拟对象的某个方法是否被调用,以及调用的次数。

  any():在设定模拟行为(如when)或验证(如verify)时,用于表示任何类型和值的参数。

  eq(T value):用于指定方法调用时期望的具体参数值。

  doNothing():用于模拟void方法时,指定该方法不执行任何操作。

  doThrow(Throwable... toBeThrown):用于模拟方法调用时抛出异常。

  spy(T object):创建一个真实对象的“间谍”或“spy”。这允许你在真实对象上“监视”方法调用,同时还能够覆盖某些方法的行为。

  ArgumentCaptor<T>:用于捕获方法调用时传递的参数,以便后续进行断言。

  times(int wantedNumberOfInvocations):与verify方法一起使用,用于指定某个方法被调用的具体次数。

  never():与verify一起使用,用于验证某个方法从未被调用过。

  示例

  假设我们有一个PaymentService类,它依赖于PaymentProcessor接口:

  public class PaymentService {      private PaymentProcessor processor;         public PaymentService(PaymentProcessor processor) {          this.processor = processor;      }         public boolean process(double amount) {          return processor.processPayment(amount);      }  }

 下面是如何使用JUnit 5和Mockito来测试PaymentService类:

  import org.junit.jupiter.api.Test;  import org.junit.jupiter.api.extension.ExtendWith;  import org.mockito.Mock;  import org.mockito.junit.jupiter.MockitoExtension;  import static org.mockito.Mockito.*;  import static org.junit.jupiter.api.Assertions.*;     @ExtendWith(MockitoExtension.class)  public class PaymentServiceTest {         @Mock      PaymentProcessor processor;         @Test      public void testProcessPayment() {          // 设置          PaymentService service = new PaymentService(processor);          double amount = 100.0;          when(processor.processPayment(amount)).thenReturn(true);             // 执行          boolean result = service.process(amount);             // 验证          assertTrue(result);          verify(processor).processPayment(amount);      }  }

  在这个例子中,我们使用@Mock来创建PaymentProcessor的模拟对象,并使用when(...).thenReturn(...)来定义当调用processPayment方法时应返回的值。然后,我们执行process方法,并使用assertTrue来验证结果是否符合预期。最后,我们使用verify来确认processPayment方法是否被正确调用。

  3.JUnit 5 进阶用法

  参数化测试(Parameterized Tests)

  参数化测试允许你使用不同的参数多次运行同一个测试。这对于需要验证多种输入条件的方法特别有用。

  @ParameterizedTest  @ValueSource(strings = {"Hello", "JUnit"})  void withValueSource(String word) {      assertNotNull(word);  }

  动态测试(Dynamic Tests)

  JUnit 5允许你动态生成测试,这些测试可以在运行时根据代码逻辑来决定。​​​​​​​

  @TestFactory  Collection<DynamicTest> dynamicTests() {      return Arrays.asList(          dynamicTest("Add test", () -> assertEquals(2, Math.addExact(1, 1))),          dynamicTest("Multiply Test", () -> assertEquals(4, Math.multiplyExact(2, 2)))      );  }

  嵌套测试(Nested Tests)

  使用@Nested注解,你可以将相关的测试组织在一起作为一个组在外层测试类中运行。​​​​​​​

  @Nested  class WhenNew {      @Test      void isEmpty() {          assertEquals(0, new ArrayList<>().size());      }      @Nested      class AfterAddingAnElement {          @Test          void isNotEmpty() {              List<Object> list = new ArrayList<>();              list.add(new Object());              assertEquals(1, list.size());          }      }  }

  超时测试

  JUnit 5允许你为测试设置超时时间,确保测试在给定时间内完成。如果超出指定时间,测试将失败。​​​​​​

  @Test  @Timeout(value = 500, unit = TimeUnit.MILLISECONDS)  void timeoutTest() {      // 模拟一个耗时的操作      // 如果操作超过500毫秒,则测试失败  }

  重复测试

  如果你想对一个测试方法进行多次执行以确保其稳定性或寻找潜在的偶发问题,可以使用@RepeatedTest注解。​​​​​​​

  @RepeatedTest(5)  void repeatTest() {      // 这个测试会运行5次  }

  条件执行

  JUnit 5提供了多种条件执行测试的方法,这些方法可以基于不同的条件来决定是否执行某个测试,例如操作系统类型、环境变量或Java版本。

  @Test  @EnabledOnOs(OS.WINDOWS)  void onlyOnWindows() {      // 仅在Windows操作系统上运行  }  @Test  @EnabledIfSystemProperty(named = "user.name", matches = "yourUserName")  void onlyForSpecificUser() {      // 仅当系统用户名匹配时运行  }

  4.Mockito 进阶用法

  使用@Spy进行部分模拟

  有时你可能需要模拟类的某些方法,而保持其他方法的实际行为。@Spy注解允许你这样做。​​​​​​​

  @Spy  List<String> spyList = new ArrayList<>();  @Test  void testSpy() {      spyList.add("one");      spyList.add("two");      verify(spyList).add("one");      verify(spyList).add("two");      assertEquals(2, spyList.size()); // 实际调用方法      // 修改方法行为      doReturn(100).when(spyList).size();      assertEquals(100, spyList.size()); // 方法行为被改变  }

  参数捕获(Argument Captors)

  有时在验证方法调用时,你可能对方法调用的具体参数值感兴趣。@Captor注解和ArgumentCaptor类允许你捕获和检查这些值。​​​​​​​

  @Mock  List<String> mockList;  @Captor  ArgumentCaptor<String> argCaptor;  @Test  void argumentCaptorTest() {      mockList.add("one");      verify(mockList).add(argCaptor.capture());      assertEquals("one", argCaptor.getValue());  }

  连续调用的不同返回值

  有时候,你可能需要一个方法在连续调用时返回不同的值。Mockito允许你通过thenReturn()方法链来实现这一点。​​​​​​​

  when(mockList.size()).thenReturn(0).thenReturn(1);  assertEquals(0, mockList.size());  assertEquals(1, mockList.size());

  验证调用次数

  验证一个方法被调用了特定次数。​​​​​​​

  mockList.add("once");  mockList.add("twice");  mockList.add("twice");  verify(mockList).add("once");  verify(mockList, times(2)).add("twice");  verify(mockList, never()).add("never happened");

  模拟静态方法(需要Mockito 3.4.0及以上版本)

  从Mockito 3.4.0开始,你可以使用mockStatic来模拟静态方法。这是通过try-with-resources语句来实现的,以确保静态mock在使用后被正确关闭。​​​​​​​

  try (MockedStatic<UtilityClass> mockedStatic = mockStatic(UtilityClass.class)) {      mockedStatic.when(UtilityClass::someStaticMethod).thenReturn("mocked response");      assertEquals("mocked response", UtilityClass.someStaticMethod());      // 静态方法被模拟期间的行为  }  // 在这个块之外,静态方法恢复原有行为

  模拟final方法和类

  Mockito 2.x开始支持模拟final方法和类。为了启用这个功能,你需要在src/test/resources/mockito-extensions目录下创建一个名为org.mockito.plugins.MockMaker的文件,并在文件中添加一行内容:

  mock-maker-inline

  这样配置后,Mockito就可以模拟final类和方法了。

  使用BDDMockito进行行为驱动开发

  BDDMockito提供了一种基于行为驱动开发(BDD)的语法来编写Mockito测试,使得测试更加可读。

  @Test  void bddStyleTest() {      // 给定      BDDMockito.given(mockList.size()).willReturn(2);      // 当      int size = mockList.size();      // 那么      BDDMockito.then(mockList).should().size();      assertEquals(2, size);  }

  5. @Mock 、@InjectMocks的原理

  @Mock

  · 原理:@Mock注解告诉Mockito框架为标注的字段生成一个模拟对象。这个模拟对象是动态生成的代理对象,它拦截对任何非final方法的调用,并允许测试者通过Mockito的API来配置这些调用的行为(例如返回特定的值或抛出异常)。

  · 如何工作:当测试初始化时(例如,通过使用MockitoAnnotations.initMocks(this)方法或JUnit 5的@ExtendWith(MockitoExtension.class)),Mockito会扫描测试类中所有使用@Mock注解的字段,并为它们创建模拟对象。这些模拟对象默认不执行任何实际的代码逻辑,它们的行为完全由测试者通过Mockito的API来控制。

  @InjectMocks

  · 原理:@InjectMocks注解用于自动将@Mock(或@Spy)注解创建的模拟对象注入到被注解的字段中。Mockito会尝试通过构造器注入、属性注入或setter方法注入的方式,将模拟对象注入到@InjectMocks标注的实例中。

  · 如何工作:

  构造器注入:Mockito首先尝试使用包含最多参数的构造器来创建实例。如果构造器的参数能够与已声明的模拟对象匹配,这些模拟对象将被用作构造器参数。

  属性注入:如果构造器注入不适用或不成功,Mockito会尝试直接设置实例中与模拟对象类型相匹配的属性。

  Setter注入:最后,如果属性注入不成功,Mockito会尝试通过调用匹配的setter方法来注入模拟对象。

最后感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走! 

软件测试面试文档

我们学习必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有字节大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。


http://www.ppmy.cn/devtools/111063.html

相关文章

使用 Python 读取 Excel 数据的详细教程

使用 Python 读取 Excel 数据的详细教程 Python 提供了多种读取 Excel 文件的方式&#xff0c;最常用的库是 pandas 和 openpyxl。下面我将详细介绍如何使用这两个库来读取 Excel 文件&#xff0c;并包含一些实用示例&#xff0c;帮助你撰写博客。 1. 安装必要的依赖 首先&a…

[Golang] goroutine

[Golang] goroutine 文章目录 [Golang] goroutine并发进程和线程协程 goroutine概述如何使用goroutine 并发 进程和线程 谈到并发&#xff0c;大多都离不开进程和线程&#xff0c;什么是进程、什么是线程&#xff1f; 进程可以这样理解&#xff1a;进程就是运行着的程序&…

SpringBoot景区分时预约系统---附源码77951

目 录 摘要 1 绪论 1.1 选题背景与意义 1.2开发现状 1.3论文结构与章节安排 2 景区分时预约系统系统分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2 经济可行性分析 2.1.3 法律可行性分析 2.2 系统功能分析 2.2.1 功能性分析 2.2.2 非功能性分析 2.3 系统用例分…

11、LLaMA-Factory自定义数据集微调

1、数据集定义 针对实际的微调需求&#xff0c;使用专门针对业务垂直领域的私有数据进行大模型微调才是我们需要做的。因此&#xff0c;我们需要探讨如何在LLaMA-Factory项目及上述创建的微调流程中引入自定义数据集进行微调。**对于LLaMA-Factory项目&#xff0c;目前仅支持两…

【iOS】Masnory的简单学习

目录 前言常用的布局方法Masnory什么是MasnoryMasnory的安装Masnory常用的方法基本约束方法约束创建方法尺寸和位置约束边距和间距尺寸和比例约束组合激活和更新约束示例代码 使用时出现的问题 总结 前言 在暑假的项目编写过程中&#xff0c;大量使用到了视图布局&#xff0c;笔…

一篇带你速通差分算法(C/C++)

个人主页&#xff1a;摆烂小白敲代码 创作领域&#xff1a;算法、C/C 持续更新算法领域的文章&#xff0c;让博主在您的算法之路上祝您一臂之力 欢迎各位大佬莅临我的博客&#xff0c;您的关注、点赞、收藏、评论是我持续创作最大的动力 差分算法是一种在计算机科学中常用的算法…

备份还原 本地所有的Docker 镜像并且在另一台机器上还原

备份命令 并且显示进度 backup_docker_images.sh sudo yum install jq chmod x backup_docker_images.shsudo ./backup_docker_images.sh#!/bin/bash# 指定备份目录 backup_dir"/app/dockerImageBackup/Images"# 创建备份目录&#xff0c;如果不存在的话 mkdir -p …

COCOS:(飞机大战11)主机注册碰撞事件,主机发生碰撞,闪烁,坠落销毁

前几节Player.ts中已经编写的触摸&#xff0c;子弹单双发的逻辑&#xff0c; 这节记录受伤闪烁&#xff0c;和结束坠机的代码。 /*** collider:Collider2D 注册碰撞事件* hp 主机生命值* anim 主机发生碰撞播放动画* animHit …

vue3 项目中使用git

一.vue项目创建 二.创建本地仓库并和远程仓库进行绑定 在vue3-project-git 项目文件夹下 初始化一个新的Git仓库&#xff0c;可以看到初始化成功之后就会出现一个.git文件&#xff0c;该文件包含所有必要的 Git 配置和版本控制信息。 创建远程仓库: 打开gitee ,点击右上角 ‘…

HarmonyOS】ArkTS学习之基于TextTimer的简易计时器的elapsedTime最小时间单位问题

本文旨在纪录自己对TextTimer使用过程的疑惑问题 我在查看教程时候&#xff0c;发现很多博客在onTimer(event: (utc: number, elapsedTime: number) > void) 这里提到elapsedTime&#xff1a;计时器经过的时间&#xff0c;单位为毫秒。我不清楚是否为版本问题。 在我查看ver…

django ubuntu 踩坑集锦

目录 1 ubantu mysql查看表结构2 导入同级目录文件出现未解析引用错误3 第三方包——tinymce富文本编辑器4 verbose_name,verbose_name_plural5 搜索路径的添加6 auto_now_add 和 auto_now7 auth_user的表结构8 在 Django 中定义 ForeignKey 字段时&#xff0c;必须指定 on_del…

春日美食:基于SpringBoot的在线订餐系统

1 绪论 1.1 研究背景 随着互联网技术的快速发展&#xff0c;网络时代的到来&#xff0c;网络信息也将会改变当今社会。各行各业在日常企业经营管理等方面也在慢慢的向规范化和网络化趋势汇合[13]。电子商务必将成为未来商务的主流&#xff0c;因此对于餐饮行业来说&#xff0c;…

MongoDB介绍

1. MongoDB 概述 MongoDB 是一种基于文档存储的开源 NoSQL 数据库&#xff0c;采用了灵活的文档模型来存储数据&#xff0c;数据结构可以根据需要动态调整。这使得 MongoDB 非常适合处理快速变化的、非结构化的数据。它以高性能、可扩展性和灵活性著称&#xff0c;是现代应用程…

EasyExcel 文件导出 - 合并某些列值相同的行

文章目录 EasyExcel 文件导出 - 合并某些列值相同的行最终效果实现思路创建单元格合并的策略类使用 EasyExcel 文件导出 - 合并某些列值相同的行 在数据处理与文件导出的过程中&#xff0c;我们常常会遇到各种特定的需求。今天&#xff0c;我们就来探讨一下使用 EasyExcel 进行…

ArcGIS Pro SDK (十三)地图创作 1 地图

ArcGIS Pro SDK (十三)地图创作 1 地图 文章目录 ArcGIS Pro SDK (十三)地图创作 1 地图1 地图1.1 获取活动地图1.2 使用默认底图图层创建新地图1.3 在工程中查找地图并将其打开1.4 打开 web 地图1.5 获取地图窗格1.6 从地图窗格中获取唯一的地图列表1.7 更改地图名称1.8 重…

Django:Python高级Web框架详解及参数设置

Django是一个高级的Python Web框架&#xff0c;它鼓励快速开发和简洁实用的设计。Django遵循MVC设计模式&#xff0c;提供了一套完整的解决方案&#xff0c;用于构建复杂的、数据库驱动的网站。 Django的主要特点 自动管理数据库&#xff1a;通过ORM&#xff08;对象关系映射…

2024/9/11学校教的响应式前端能学到什么?

9.11 1&#xff09;砌砖 确定整体框架&#xff0c;而不是想到一点写一点&#xff0c;类似盖大楼&#xff0c;不是想到哪盖到哪&#xff0c;先砌砖&#xff0c;再装修 砌砖前先划分好砌砖范围(初始化样式) 清除body自带的内外边距 * { margin: 0; padding: 0; }去掉li的小圆点…

大数据决策分析平台建设方案(可编辑的56页PPT)

引言&#xff1a;在当今信息爆炸的时代&#xff0c;大数据已成为企业决策制定、业务优化与市场洞察的重要驱动力。为了充分挖掘大数据的潜在价值&#xff0c;提升决策效率与精准度&#xff0c;构建一套高效、灵活、可扩展的大数据决策分析平台显得尤为重要。通过大数据分析平台…

16. MyBatis的延迟加载机制是什么?如何配置?有哪些优缺点?

延迟加载&#xff08;Lazy Loading&#xff09;是MyBatis提供的一种机制&#xff0c;用于优化数据库查询性能。在启用延迟加载时&#xff0c;某些关联对象或集合只有在被实际访问时才会触发数据库查询&#xff0c;而不是在主对象加载时立即加载。这种机制可以减少不必要的数据库…

React实现虚拟列表的优秀库介绍

在 React 中&#xff0c;有一些优秀的库可以帮助你实现高效的虚拟列表渲染。以下是几个常用的库&#xff1a; 1. react-window react-window 是一个轻量级的虚拟列表库&#xff0c;适用于大多数虚拟列表需求。它提供了简单易用的 API 和良好的性能。 安装 npm install reac…