详细分析SpringBootTest中的测试类(附Demo)

news2024/11/26 9:27:55

目录

  • 前言
  • 1. 基本知识
  • 2. Demo
  • 3. 实战
    • 3.1 项目测试
    • 3.2 功能测试

前言

书写测试类,一般只需要加入@Test即可,但是结合Springboot项目来整体测试对应需要怎么下手

详细的Java知识点推荐阅读:java框架 零基础从入门到精通的学习路线 附开源项目面经等(超全)

1. 基本知识

涉及测试类的基本注解分析如下:

一、@SpringBootTest:加载完整的Spring应用上下文
使用场景:需要测试整个Spring Boot应用时使用,确保所有的Bean都能被正确加载

@SpringBootTest
public class MyApplicationTests {
    // 测试方法
}

二、@RunWith(SpringRunner.class):指定测试运行器,这里使用SpringRunner来运行Spring Boot测试
使用场景:结合JUnit 4使用时指定测试运行器

@RunWith(SpringRunner.class)
public class MyApplicationTests {
    // 测试方法
}

三、@Autowired:自动注入Spring管理的Bean
使用场景:在测试类中需要注入服务、仓库等Bean时使用

@Autowired
private MyService myService;

四、@Slf4j:使用Lombok提供的日志记录功能
使用场景:需要在类中记录日志时使用

@Slf4j
public class MyApplicationTests {
    // 可以使用log.info(), log.debug()等记录日志
}

五、@Test:标记一个方法为测试方法
使用场景:任何需要进行单元测试的方法都应使用该注解

@Test
public void testMethod() {
    // 测试逻辑
}

常出现的情况有如下:

  1. 注入的Bean为null
    使用@Autowired注入的Bean为null
    》》》》确保被注入的类被Spring管理(例如,使用@Service、@Component等注解),确保测试类使用@SpringBootTest注解以加载完整的Spring上下文

  2. 测试类无法加载上下文
    测试类无法加载Spring上下文,抛出BeanCreationException等异常
    》》》》确保@SpringBootTest注解的classes属性指向正确的主应用程序类,例如AccidentApplication.class

2. Demo

结合实战中的Demo进行讲解

import com.sinosoft.springbootplus.AccidentApplication;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@SpringBootTest(classes = AccidentApplication.class)
@Slf4j
public class TestClass {

    @Autowired
    private XXXService xxxService;

    @Test
    public void testDemo() {
        // 示例测试代码
        log.info("Starting testDemo");
        // 调用XXXService的某个方法,并进行断言
        String result = xxxService.someMethod();
        assertNotNull(result);
        log.info("testDemo completed");
    }
}

其中的注意事项如下:

  1. 类路径:确保AccidentApplication类在你的类路径下,并且可以被正确加载
  2. Service注入:XXXService必须在Spring上下文中被定义为一个Bean,否则@Autowired注入会失败
  3. JUnit版本:确保项目使用JUnit 5(JUnit Jupiter)或者JUnit 4,不要混用
    上面的代码中使用的是JUnit 5,如果你需要使用JUnit 4,请使用org.junit.Test和org.junit.runner.RunWith
  4. 日志使用:通过@Slf4j注解,可以在测试方法中方便地记录日志,这有助于调试

而且基本的项目结构如下:

src
├── main
│   └── java
│       └── com
│           └── manong
│               └── springbootplus
│                   ├── AccidentApplication.java
│                   └── service
│                       └── XXXService.java
└── test
    └── java
        └── com
            └── manong
                └── springbootplus
                    └── TestClass.java

3. 实战

3.1 项目测试

Demo与实战中大同小异,上述使用的是JUnit 5 ,下面使用一个JUnit 4进行演示

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;


@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = YudaoServerApplication.class)
public class projectTest {

    @Test
    public void testUsingTosDataSource() {
    }

}

实际截图如下:

在这里插入图片描述

3.2 功能测试

实战中可能有多个模块,对应的配置需如下:

package cn.example.module.dangerous.service.enterpriseregistry;

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;

import javax.annotation.Resource;

import cn.example.framework.test.core.ut.BaseDbUnitTest;

import cn.example.module.dangerous.controller.admin.enterpriseregistry.vo.*;
import cn.example.module.dangerous.dal.dataobject.enterpriseregistry.EnterpriseRegistryDO;
import cn.example.module.dangerous.dal.mysql.enterpriseregistry.EnterpriseRegistryMapper;
import cn.example.framework.common.pojo.PageResult;

import javax.annotation.Resource;
import org.springframework.context.annotation.Import;
import java.util.*;
import java.time.LocalDateTime;

import static cn.hutool.core.util.RandomUtil.*;
import static cn.example.module.dangerous.enums.ErrorCodeConstants.*;
import static cn.example.framework.test.core.util.AssertUtils.*;
import static cn.example.framework.test.core.util.RandomUtils.*;
import static cn.example.framework.common.util.date.LocalDateTimeUtils.*;
import static cn.example.framework.common.util.object.ObjectUtils.*;
import static cn.example.framework.common.util.date.DateUtils.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

/**
 * {@link EnterpriseRegistryServiceImpl} 的单元测试类
 *
 * @author 管理员
 */
@Import(EnterpriseRegistryServiceImpl.class)
public class EnterpriseRegistryServiceImplTest extends BaseDbUnitTest {

    @Resource
    private EnterpriseRegistryServiceImpl enterpriseRegistryService;

    @Resource
    private EnterpriseRegistryMapper enterpriseRegistryMapper;

    @Test
    public void testCreateEnterpriseRegistry_success() {
        // 准备参数
        EnterpriseRegistrySaveReqVO createReqVO = randomPojo(EnterpriseRegistrySaveReqVO.class).setId(null);

        // 调用
        Long enterpriseRegistryId = enterpriseRegistryService.createEnterpriseRegistry(createReqVO);
        // 断言
        assertNotNull(enterpriseRegistryId);
        // 校验记录的属性是否正确
        EnterpriseRegistryDO enterpriseRegistry = enterpriseRegistryMapper.selectById(enterpriseRegistryId);
        assertPojoEquals(createReqVO, enterpriseRegistry, "id");
    }

    @Test
    public void testUpdateEnterpriseRegistry_success() {
        // mock 数据
        EnterpriseRegistryDO dbEnterpriseRegistry = randomPojo(EnterpriseRegistryDO.class);
        enterpriseRegistryMapper.insert(dbEnterpriseRegistry);// @Sql: 先插入出一条存在的数据
        // 准备参数
        EnterpriseRegistrySaveReqVO updateReqVO = randomPojo(EnterpriseRegistrySaveReqVO.class, o -> {
            o.setId(dbEnterpriseRegistry.getId()); // 设置更新的 ID
        });

        // 调用
        enterpriseRegistryService.updateEnterpriseRegistry(updateReqVO);
        // 校验是否更新正确
        EnterpriseRegistryDO enterpriseRegistry = enterpriseRegistryMapper.selectById(updateReqVO.getId()); // 获取最新的
        assertPojoEquals(updateReqVO, enterpriseRegistry);
    }

    @Test
    public void testUpdateEnterpriseRegistry_notExists() {
        // 准备参数
        EnterpriseRegistrySaveReqVO updateReqVO = randomPojo(EnterpriseRegistrySaveReqVO.class);

        // 调用, 并断言异常
        assertServiceException(() -> enterpriseRegistryService.updateEnterpriseRegistry(updateReqVO), ENTERPRISE_REGISTRY_NOT_EXISTS);
    }

    @Test
    public void testDeleteEnterpriseRegistry_success() {
        // mock 数据
        EnterpriseRegistryDO dbEnterpriseRegistry = randomPojo(EnterpriseRegistryDO.class);
        enterpriseRegistryMapper.insert(dbEnterpriseRegistry);// @Sql: 先插入出一条存在的数据
        // 准备参数
        Long id = dbEnterpriseRegistry.getId();

        // 调用
        enterpriseRegistryService.deleteEnterpriseRegistry(id);
        // 校验数据不存在了
        assertNull(enterpriseRegistryMapper.selectById(id));
    }

    @Test
    public void testDeleteEnterpriseRegistry_notExists() {
        // 准备参数
        Long id = randomLongId();

        // 调用, 并断言异常
        assertServiceException(() -> enterpriseRegistryService.deleteEnterpriseRegistry(id), ENTERPRISE_REGISTRY_NOT_EXISTS);
    }

    @Test
    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
    public void testGetEnterpriseRegistryPage() {
        // mock 数据
        EnterpriseRegistryDO dbEnterpriseRegistry = randomPojo(EnterpriseRegistryDO.class, o -> { // 等会查询到
            o.setEnterpriseName(null);
            o.setCreditCode(null);
            o.setRegistrant(null);
            o.setCreateTime(null);
            o.setEnterpriseCode(null);
            o.setContactNumber(null);
        });
        enterpriseRegistryMapper.insert(dbEnterpriseRegistry);
        // 测试 enterpriseName 不匹配
        enterpriseRegistryMapper.insert(cloneIgnoreId(dbEnterpriseRegistry, o -> o.setEnterpriseName(null)));
        // 测试 creditCode 不匹配
        enterpriseRegistryMapper.insert(cloneIgnoreId(dbEnterpriseRegistry, o -> o.setCreditCode(null)));
        // 测试 registrant 不匹配
        enterpriseRegistryMapper.insert(cloneIgnoreId(dbEnterpriseRegistry, o -> o.setRegistrant(null)));
        // 测试 createTime 不匹配
        enterpriseRegistryMapper.insert(cloneIgnoreId(dbEnterpriseRegistry, o -> o.setCreateTime(null)));
        // 测试 enterpriseCode 不匹配
        enterpriseRegistryMapper.insert(cloneIgnoreId(dbEnterpriseRegistry, o -> o.setEnterpriseCode(null)));
        // 测试 contactNumber 不匹配
        enterpriseRegistryMapper.insert(cloneIgnoreId(dbEnterpriseRegistry, o -> o.setContactNumber(null)));
        // 准备参数
        EnterpriseRegistryPageReqVO reqVO = new EnterpriseRegistryPageReqVO();
        reqVO.setEnterpriseName(null);
        reqVO.setCreditCode(null);
        reqVO.setRegistrant(null);
        reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
        reqVO.setEnterpriseCode(null);
        reqVO.setContactNumber(null);

        // 调用
        PageResult<EnterpriseRegistryDO> pageResult = enterpriseRegistryService.getEnterpriseRegistryPage(reqVO);
        // 断言
        assertEquals(1, pageResult.getTotal());
        assertEquals(1, pageResult.getList().size());
        assertPojoEquals(dbEnterpriseRegistry, pageResult.getList().get(0));
    }

}

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

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

相关文章

[CAN] 通讯协议手动解析与手动打包 [手撕编码格式]

手动解析与手动打包 一、Intel格式编码1.1 报文解析。1.2 报文打包二、Motorola格式通讯协议2.1 报文解析。2.2 报文打包🙋 前言 CAN有两种编码格式:Intel编码格式 和 Motorola编码格式,本教程将分别对两种格式进行手动解析与手动打包。 一、Intel格式编码 假设已知雷达CAN…

【C++题解】1721. 输出个位为5或者个位为8数

问题&#xff1a;1721. 输出个位为5或者个位为8数 类型&#xff1a;简单循环 题目描述&#xff1a; 请从小到大输出 1∼n 中所有个位为 5 或者个位为8 的所有的整数&#xff0c;每行 1 个。 比如&#xff0c;假设 n20&#xff0c;那么满足条件的数输出如下&#xff1a; 5 8 1…

【多线程】如何解决线程安全问题?

&#x1f970;&#x1f970;&#x1f970;来都来了&#xff0c;不妨点个关注叭&#xff01; &#x1f449;博客主页&#xff1a;欢迎各位大佬!&#x1f448; 文章目录 1. synchronized 关键字1.1 锁是什么1.2 如何加锁1.3 synchronized 修饰方法1) 修饰普通成员方法2) 修饰静态…

Rust 程序设计语言学习——泛型、Trait和生命周期

每一种编程语言都有高效处理重复概念的工具。在 Rust 中其工具之一就是泛型。泛型是具体类型或其他属性的抽象替代。 Trait 定义了某个特定类型拥有可能与其他类型共享的功能。可以通过 Trait 以一种抽象的方式定义共同行为。可以使用 trait bounds 指定泛型是任何拥有特定行为…

Java——IO流(一)-(8/8):释放资源-try-catch-finally、try-catch-resource

目录 try-catch-finally 介绍 实例演示1 实例演示2 try-catch-resource 介绍 实例演示 try-catch-finally 介绍 普通的释放流的方法可能会因中间的异常或是其他原因&#xff0c;导致程序执行不到释放流的代码就结束了&#xff0c;会有资源浪费的风险&#xff0c;所以建…

入门JavaWeb之 JavaBean 实体类

JavaBean 有特定写法&#xff1a; 1.必须有一个无参构造 2.属性必须私有 3.必须有对应的 get/set 方法 一般用来和数据库的字段做映射 ORM&#xff1a;对象关系映射 表->类 字段->属性 行记录->对象 连接数据库 没有的话去 Settings -> Plugins 搜索 Data…

JavaScript--js基础(详细 全面)

目录 前言: JavaScript 是什么&#xff1f;JavaScript 简介 1.JavaScript历史 2.JavaScript 具有以下特点 第一个JavaScript程序 1.在脚本文件中编写JavaScript代码 2.JavaScript代码执行顺序 基本语法 1.变量 2.数据类型 3.算术运算符 4.赋值运算 5.字符串运算符 6…

GoSync+华为智能穿戴使用指导

GoSync官方简介&#xff1a; GoSync 是一款免费应用程序&#xff0c;主要用于将您的可穿戴设备中的步行、跑步、骑自行车和游泳等活动数据同步到您的 Google Fit 和其他健身平台。在开始同步数据之前&#xff0c;您需要将您的可穿戴设备账户与您的健身平台账户连接起来。在创建…

Modbus为何要转成EtherCAT

1. Modbus是什么&#xff1f; Modbus是一种工业通信协议&#xff0c;广泛应用于工业自动化领域。它支持多种通信方式&#xff0c;包括RS-232、RS-485和TCP/IP等。Modbus协议简单易用&#xff0c;能够实现设备之间的数据交换和控制命令的传输。然而&#xff0c;它在数据传输速率…

微软推出最新视觉基础模型Florence-2 可在浏览器运行

据微软官方消息&#xff0c;微软推出视觉基础模型Florence-2&#xff0c;该模型现已能够在支持WebGPU的浏览器中100%本地运行。Florence-2-base-ft是一个拥有2.3亿参数的视觉基础模型&#xff0c;采用基于提示的方法来处理广泛的视觉和视觉语言任务。 该模型支持多种功能&…

FME实现批量合并shapefile文件数据,并提取原文件名,输出到属性表字段中的解决方法

目录 一、实现效果 二、实现过程 1.读取数据 2.暴露文件名属性 3.设置文件名字段 4.输出成果 5.模板的使用 三、总结 今天来介绍如何使用FME软件来实现对多个shapefile数据进行批量合并&#xff0c;同时提取原文件名并存储到合并后shapefile数据属性表字段中的方法&…

由监官要求下架docker hub镜像导致无法正常拉取镜像

问题&#xff1a;下载docker镜像超时 error pulling image configuration: download failed after attempts6: dial tcp 202.160.128.205:443: i/o timeout解决办法&#xff1a;配置daemon.json [rootbogon aihuidi]# cat /etc/docker/daemon.json {"registry-mirrors&qu…

<项目> 日志系统

目录 前言&#xff1a; 一、项目介绍 二、为什么需要日志系统 三、日志系统技术实现 &#xff08;一&#xff09;同步写⽇志 &#xff08;二&#xff09;异步写⽇志 四、前置技术学习 &#xff08;一&#xff09;不定参宏函数 &#xff08;二&#xff09;C语言不定参数…

mysql安装创建数据库防止踩坑

为了安装MySQL的家人们走弯路&#xff0c;稍微有些啰嗦&#xff0c;讲述我安装的时遇到的问题&#xff0c;如何解决。仔细看看离成功不远。 mysql下载链接 MySQL :: Download MySQL Community Server windows下安装mysql-8.0.29-winx64&#xff0c;下载安装包后解压到文件夹中…

摄影后期色彩管理流程(Lightroom篇)

在摄影后期处理中&#xff0c;色彩管理是确保图像从捕捉到输出的一致性和准确性的关键。Lightroom 和 Photoshop 其实已经将这套色彩管理流程作为默认选项&#xff0c;如果实质操作时仍存在色彩偏差的问题&#xff0c;可参考以下内容。 ProPhoto RGB > Adobe RGB > sRGB …

clickhouse count和uniqCombined

count(distinct ) 和 uniqCombined 获取去重后的总数。 去重&#xff1a;order by distinct argMax group by 哪个好&#xff1f;&#xff1f; clickhouse数据去重函数介绍&#xff08;count distinct&#xff09;_clickhouse distinct-CSDN博客

[论文阅读笔记33] Matching Anything by Segmenting Anything (CVPR2024 highlight)

这篇文章借助SAM模型强大的泛化性&#xff0c;在任意域上进行任意的多目标跟踪&#xff0c;而无需任何额外的标注。 其核心思想就是在训练的过程中&#xff0c;利用strong augmentation对一张图片进行变换&#xff0c;然后用SAM分割出其中的对象&#xff0c;因此可以找到一组图…

注意力机制在大语言模型中的应用

在大语言模型中&#xff0c;注意力机制&#xff08;Attention Mechanism&#xff09;用于捕获输入序列中不同标记&#xff08;token&#xff09;之间的关系和依赖性。这种机制可以动态地调整每个标记对当前处理任务的重要性&#xff0c;从而提高模型的性能。具体来说&#xff0…

Qt通过句柄获取其它进程控件实例

1.通过spy获取想要获取控件的句柄id 通过spy获取另一个软件的文本框的句柄 2.Qt写代码&#xff0c; 根据句柄获取文本框的内容 void getTextFromExternalWindow(HWND hwnd) {const int bufferSize 256;TCHAR buffer[bufferSize];// 获取窗口文本内容int length GetWindowT…

svn明明都在环境变量中添加了,但还是无法在cmd中生效

svn明明都在环境变量中添加了&#xff0c;但还是无法在cmd中生效 cmd显示原因问题解决 cmd显示 svn不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件 原因 安装svn一直点下一步下一步…&#xff0c;没有勾选command line client。 问题解决 1.按下winx&…