Junit 集成测试

前言 

现在作者说明一下,作者需要开发一个简单的Vue+Springboot前后端分离实验,想要尽量将测试的流程应用到这样的系统中。单元测试请见Junit单元测试_Joy T的博客-CSDN博客,而单元测试加上mock呢,最多也只能测试一下Service层的业务逻辑,对于数据访问层的代码,比如save/insert等等,用单元测试不是很到位。Junit+mock请见Mock简单应用_Joy T的博客-CSDN博客

首先因为这些数据访问层的层数几乎已经到底层,无法使用mock去模拟一个下层对象。其次,对于数据访问层,确实应该测试一下与数据库真实的连接了,这种接近于实际情况的交互还是使用集成测试会好一些。


集成测试与单元测试应用场景对比

对于何时应该进行单元测试和集成测试,确实存在一些争议和不同的做法。但大体上,以下是一个通常的建议和理由:

单元测试

  • 业务逻辑层(例如,服务层、工具类等):这是单元测试最有价值的地方。你可以测试逻辑是否正确、边界条件是否得到处理、异常是否正确抛出等。
  • 自定义工具或库:如果你开发了一些自定义的工具类或库函数,那么对它们进行单元测试是很有意义的。
  • 复杂的数据转换或处理:如果你的代码需要进行复杂的数据转换或处理(例如,将数据从一种格式转换为另一种格式),那么单元测试可以帮助确保这些转换或处理是正确的。

集成测试

  • 数据访问层(例如,使用MyBatis的Mapper):对于数据访问层,通常集成测试更有意义。你可能会想知道SQL查询是否正确、返回的数据是否符合预期、事务是否正确处理等。对于JavaWeb来说,数据访问层通常指Dao层;而对于Springboot来说,数据访问层就是Repository。
  • 外部服务交互:当你的代码与外部服务(例如,其他的微服务、第三方API等)交互时,集成测试可以帮助确保这些交互的正确性和稳定性。
  • 整体应用流程:测试应用的整体流程,确保各个组件、服务或模块之间的交互是正确的。

集成测试是不是就是接口测试?

  • 集成测试:它关注的是多个组件或系统的部分(如两个模块、一个服务和一个数据库等)如何一起工作。例如,你可能有一个测试来确保你的UserRepository能够正确地从数据库中检索数据。

  • 接口测试:接口测试或API测试,特指测试软件的接口,确保它们正常工作、可靠并且满足其预期的功能。这些接口可能是HTTP REST API、SOAP web服务或任何其他类型的API。

简言之,所有的接口测试都可以看作是集成测试,但并非所有的集成测试都是接口测试。集成测试包括的领域更大。


集成测试实例

在Spring Boot应用中,我们通常使用@DataJpaTest来进行数据访问层的集成测试。

UserRepository进行集成测试的示例

User.java (Entity)

@Entity
public class User {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String username;private String password;// getters, setters, constructors...
}

UserRepository.java

public interface UserRepository extends JpaRepository<User, Long> {Optional<User> findByUsername(String username);
}

UserRepositoryIntegrationTest.java (测试类)

@RunWith(SpringRunner.class)
@DataJpaTest
public class UserRepositoryIntegrationTest {@Autowiredprivate TestEntityManager entityManager;@Autowiredprivate UserRepository userRepository;@Testpublic void whenFindByUsername_thenReturnUser() {// givenUser john = new User("john", "123456");entityManager.persist(john);entityManager.flush();// whenOptional<User> found = userRepository.findByUsername(john.getUsername());// thenassertTrue(found.isPresent());assertEquals(found.get().getUsername(), john.getUsername());}// 其他相关的集成测试方法...
}
  • 我们使用@DataJpaTest启动一个嵌入式数据库,并自动配置Spring Data JPA
  • TestEntityManager提供了一种更为简单的方式来管理持久性上下文,并允许我们执行常见的数据库操作。

详解UserRepositoryIntegrationTest.java (测试类)

我们来逐步解释UserRepositoryIntegrationTest.java中的内容。

1. 基本设置

@RunWith(SpringRunner.class)
@DataJpaTest

@RunWith(SpringRunner.class):这个注解告诉JUnit使用Spring的测试运行器。这意味着我们可以在测试中利用Spring Boot的特性。

@DataJpaTest:这个注解专门为Spring Data JPA的测试提供支持。它会配置一个嵌入式数据库(默认是H2),并且会进行JPA的相关配置,使得我们可以直接测试与数据库的交互。这里的“集成”体现在我们实际与一个真实的数据库交互,而不是使用mock。

2. 属性和注入

@Autowired
private TestEntityManager entityManager;@Autowired
private UserRepository userRepository;

TestEntityManager:非常重要,这是专门为测试提供的实体管理器。它是JPA EntityManager的一个简化版本,用于数据库操作。我们可以使用它来添加、更新或删除测试数据。

UserRepository:这是我们要测试的Spring Data JPA repository。一个专门的固定的实体管理器,一个是我们要测试的Repository层。

3. 测试方法

@Test
public void whenFindByUsername_thenReturnUser() {// givenUser john = new User("john", "123456");entityManager.persist(john);entityManager.flush();// whenOptional<User> found = userRepository.findByUsername(john.getUsername());// thenassertTrue(found.isPresent());assertEquals(found.get().getUsername(), john.getUsername());
}

这个测试方法分为三个阶段:给定 (given),当 (when) 和那么 (then)。

  • 给定 (given): 这里,我们创建了一个新的用户对象,并使用TestEntityManager将其持久化到数据库中(persist&flush)。

  • 当 (when): 在这一步,我们试图通过UserRepositoryfindByUsername方法根据用户名找到用户。

  • 那么 (then): 这是我们的断言阶段,我们检查从UserRepository返回的值是否符合我们的预期。我们预期的是,当我们查询一个已经存在于数据库中的用户名时,UserRepository应该返回那个用户。

如果在这三个阶段中的任何一个阶段出现错误或异常,或者预期的结果与实际的结果不匹配,那么这个测试就会失败。


When 详解

 // when
Optional<User> found = userRepository.findByUsername(john.getUsername());

Optional是Java 8引入的一个容器对象它可能包含一个值,也可能不包含(即为空),就是表面意思:可选。其主要目的是提供一个明确的方式来处理null值的情况,避免NullPointerException,更加灵活,便于测试。

在我们讨论的上下文中,Optional存储的是单个User对象,不是数组,对象名为found。所以,found.get()返回的是单个User对象。

如果期望从数据库检索多个User对象,那么UserRepository的方法返回类型可能会是List<User>,而不是Optional<User>。例如:

List<User> findByUsername(String username);

在这种情况下,如果有多个与指定用户名匹配的用户,那么返回的列表将包含所有这些用户。你可以通过检查列表的大小或迭代列表中的每个用户来处理多个用户的情况。

但在大多数应用程序中,用户名通常是唯一的,所以通过用户名检索用户时,通常只返回一个用户。这也是为什么在很多情况下,你会看到Optional<User>作为返回类型。


Then 详解

    // thenassertTrue(found.isPresent());assertEquals(found.get().getUsername(), john.getUsername());

这两句代码是利用Java的Optional类以及JUnit的断言来验证测试的预期结果。这并不直接对应User类的方法,而是与UserRepository的方法以及Java的Optional类相关。

让我们详细分析:

assertTrue(found.isPresent());

这句代码使用的isPresent()方法是Optional类的方法。当我们使用Spring Data JPA的repository方法返回对象时,为了处理可能的null值(例如当对象不存在于数据库中时),通常会返回Optional<T>类型。Optional类有一个isPresent()方法,如果Optional内部包含一个非null值,它返回true,否则返回false,相当于isExist()

所以,assertTrue(found.isPresent());这行代码的意思是:我们期望UserRepository返回一个包含用户的Optional。

assertEquals(found.get().getUsername(), john.getUsername());

  • found.get(): 这是Optional类的另一个方法,它返回Optional对象内部的值(如果存在的话)。

  • getUsername(): 这是我们的User类的方法,用于获取用户的用户名。

所以,assertEquals(found.get().getUsername(), john.getUsername());这行代码的意思是:我们期望从数据库中检索到的用户(found.get())的用户名与我们原始存储的用户john的用户名相匹配。

这样,结合两句断言,我们可以测试UserReposiroty中定义的FindByUsername()方法是否能够按照用户名去得到指定用户!


SelectAll()!

当我们想测试类似selectAll这样的方法,返回的确实是一个List,一般不使用Optional类对象作为承载者。为了进行集成测试,我们可以添加一些记录到数据库,然后调用selectAll方法,最后验证返回的列表是否包含预期的记录。

以下是一个简单的示例,用于测试selectAll方法:

@RunWith(SpringRunner.class)
@DataJpaTest
public class UserRepositoryIntegrationTest {@Autowiredprivate TestEntityManager entityManager;@Autowiredprivate UserRepository userRepository;@Testpublic void testSelectAll() {// Given: 添加一些预期的记录到数据库User john = new User();john.setUsername("john");john.setPassword("john123");entityManager.persist(john);User jane = new User();jane.setUsername("jane");jane.setPassword("jane123");entityManager.persist(jane);// When: 调用selectAll方法List<User> users = userRepository.selectAll();// Then: 验证返回的列表是否包含预期的记录assertNotNull(users);assertTrue(users.size() >= 2);  // 因为你不确定测试数据库中是否有其他记录,所以我们检查是否至少有我们插入的记录// 更进一步的验证可以检查返回的用户是否真的是我们预期的那些用户assertTrue(users.stream().anyMatch(user -> user.getUsername().equals("john")));assertTrue(users.stream().anyMatch(user -> user.getUsername().equals("jane")));}
}

存储john&jane用户,调用selectAll()方法,使用断言验证selectAll查询到的信息有没有john&jane。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

React之受控组件和非受控组件以及高阶组件

一、受控组件 受控组件&#xff0c;简单来讲&#xff0c;就是受我们控制的组件&#xff0c;组件的状态全程响应外部数据 举个简单的例子&#xff1a; class TestComponent extends React.Component {constructor (props) {super(props);this.state { username: lindaidai }…

uniapp在App端如何动态修改原生导航栏?

uniapp在App端如何动态修改原生导航栏&#xff1f; 文章目录 uniapp在App端如何动态修改原生导航栏&#xff1f;page.json配置修改 buttons 文字修改按钮上的角标设置 searchInput的 focus设置 searchInput的 text 在App端可以通过得到 webview 对象&#xff0c;通过当前 webvi…

VSCode 调试 u-boot

文章目录 VSCode 调试 u-boot调试配置启动 u-boot 脚本调试界面重定向之后继续调试参考 VSCode 调试 u-boot 调试配置 参考 qemu基础篇——VSCode 配置 GDB 调试 要想调试 u-boot 只需要再添加一个 u-boot 的配置即可 {"version": "0.2.0","conf…

Flutter ☞ 常量

常量 只能被定义一次&#xff0c;并且不可修改的值叫做常量。 在 Flutter 中有两种常量修饰方法 finalconst 相同点 类型声明可以省略 final String a 123; final a 123;const String a 123; const a 123;初始化后不能再赋值 final a 123; a abc; // 错误const a …

Docker-compose和Consul

目录 1、docker-compose 简介 1.1 Docker-compose 简介 2、compose 部署 2.1 Docker Compose 环境安装 2.2 YAML 文件格式及编写注意事项 * * * * 2.3 Docker Compose配置常用字段 2.4 Docker Compose 常用命令 2.5 Docker Compose 文件结构 3、Consul 3.1 什么是…

SkyWalking 告警规则配置说明

Skywalking告警功能是在6.x版本新增的,其核心由一组规则驱动,这些规则定义在config/alarm-settings.yml 文件中。告警规则定义分为两部分: 1、告警规则:它们定义了应该如何触发度量警报,应该考虑什么条件 2、webhook(网络钩子):定义当告警触发时,哪些服务终端需要被…

unity变体收集工具

项目目录:D:\GIT\YooAsset\Temp 收集代码: using System.Collections; using System.Collections.Generic; using UnityEditor; #if UNITY_EDITOR using UnityEditor.Build; using UnityEditor.Rendering; #if UNITY_2019_4_OR_NEWER using UnityEditor.Build.Pipeline…

学习笔记|串口通信实战|简易串口控制器|sprintf函数|STC32G单片机视频开发教程(冲哥)|第二十一集(下):串口与PC通信

目录 3.串口通信实战实操简易的工作原理Tips:sprintf函数简介 总结课后练习 3.串口通信实战 做一个简易串口控制器。发送对应指令&#xff0c;让板子做相应的事情&#xff0c;或者传输数据&#xff08;文本模式下发送&#xff0c;不要选择HEX&#xff09;。 1.串口发送字符Ax\…

2020年亚太杯APMCM数学建模大赛B题美国总统的经济影响分析求解全过程文档及程序

2020年亚太杯APMCM数学建模大赛 B题 美国总统的经济影响分析 原题再现&#xff1a; 美国总统选举每四年举行一次。 2020年是美国总统大选年&#xff0c;共和党候选人唐纳德特朗普和民主党对手乔拜登竞选总统。 甲乙双方候选人在金融贸易&#xff0c;经济金融治理&#xff0c;…

云计算系统与传统计算系统的比较

随着技术的不断发展&#xff0c;云计算系统逐渐成为了企业和个人使用的主要计算方式之一。然而&#xff0c;很多人对云计算系统与传统计算系统之间的区别和相似之处还存在一些疑惑。本文将以云计算系统和传统计算系统为方向&#xff0c;探讨它们之间的异同点。 首先&#xff0…

python爬虫实战-京东商品数据

前言 大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 今天介绍一下如何用 Python 来批量获取京东商品信息&#xff01;&#xff01; 如果有什么疑惑/资料需要的可以点击文章末尾名片领取源码 第三方库: requests >>> pip install requests 开发环境: python 3.8 py…

【iOS】——用单例类封装网络请求

文章目录 一、JSONModel1.JSONModel的简单介绍2.JSONModel的使用 二、单例类和Block传值 一、JSONModel 1.JSONModel的简单介绍 JSONModel一个第三方库&#xff0c;这个库用来将网络请求到的JSON格式的数据转化成Foundation框架下的Model类的属性&#xff0c;这样我们就可以直…

mysql中date/datetime类型自动转go的时间类型time.Time

在DSN中需要加入parseTimetrue&&locLocal&#xff0c;或 charsetutf8mb4&locAsia%2FShanghai&parseTimetrue。 package main_testimport ("database/sql""fmt""testing""time"_ "github.com/go-sql-driver/mysq…

Elasticsearch基础篇(五):创建es索引并学习分析器、过滤器、分词器的作用和配置

创建es索引并学习分析器、过滤器、分词器的作用和配置 一、基础概念Elasticsearch与MySQL的类比1. ES与MySQL的结构类比图2. ES与MySQL的类比示意表格3. 索引中重要概念索引&#xff08;Index&#xff09;文档&#xff08;Document&#xff09;字段&#xff08;Field&#xff0…

2023.10.18 区别 对象 和 类对象

目录 对象 类对象 总结 对象 对象是类的实例化结果它是内存中的一块区域&#xff0c;包含了该类的属性和方法的具体值和实现对象具有唯一的标识、状态、行为通过创建类的实例&#xff0c;我们可以在程序中操作和处理具体的对象 简单实例 class Person {public int high 1…

车载开发学习——CAN总线

CAN总线又称为汽车总线&#xff0c;全程为“控制器局域网&#xff08;Controller Area Network&#xff09;”&#xff0c;即区域网络控制器&#xff0c;它将区域内的单一控制单元以某种形式连接在一起&#xff0c;形成一个系统。在这个系统内&#xff0c;大家以一种大家都认可…

Netty系列教程之NIO基础知识

近30集的孙哥视频课程&#xff0c;看完一集整理一集来的&#xff0c;内容有点多&#xff0c;请大家放心食用~ 1. 网络通讯的演变 1.1 多线程版网络通讯 在传统的开发模式中&#xff0c;客户端发起一个 HTTP 请求的过程就是建立一个 socket 通信的过程&#xff0c;服务端在建立…

VR全景图片如何拍摄制作,拍摄制作过程中要注意什么?

引言&#xff1a; VR全景图片就是通过专业的相机设备捕捉到的一个空间的高清图像&#xff0c;再经过专业工具进行拼合&#xff0c;呈现出一种环绕式的视觉效果。想象一下&#xff0c;当你站在一个完全真实的环境中&#xff0c;可以自由地转动视角&#xff0c;看到四周的景色&a…

高数定理集合啦

haha~ 最近在准备数学竞赛&#xff0c;好久没有发布笔记啦&#xff0c;今天就来一波高数里常用的定理吧&#xff0c;不全面的话后续会更新哒~ 费马定理&#xff1a;对于一个函数的局部极值点&#xff0c;如果导数存在&#xff0c;那么导数在该点处必须为零&#xff0c;即 f(x)0…

SQL数据库管理工具RazorSQL mac中文版特点与功能

RazorSQL mac是一款功能强大的SQL数据库管理工具&#xff0c;它支持多种数据库&#xff0c;包括MySQL、Oracle、Microsoft SQL Server、SQLite、PostgreSQL等。 RazorSQL mac 软件特点和功能 多种数据库支持&#xff1a;RazorSQL支持多种数据库&#xff0c;用户可以通过一个工…