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

目录

  • 前言
  • 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));
    }

}

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

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

相关文章

26、 MySQL数据库基础练习系列股票交易系统基础查询和复杂查询

5、基础查询 -- 1、查询用户信息仅显示姓名与手机号 SELECT username as 姓名,phone as 手机号 from users;-- 2、模糊查询和explain语句 alter table stocks add index stock_name_index(stock_name); explain SELECT * from stocks where stock_name like %东吴证券%; -- 3、…

提升自来水公司经济效益的策略研究

现阶段&#xff0c;如何提高自来水公司经济效益的问题已经得到社会的广泛关注。文章将立足于成本管理的角度&#xff0c;在对自来水公司生产现状分析的基础上&#xff0c;总结影响自来水公司经济效益的相关因素&#xff0c;最后从成本管理的角度出发&#xff0c;对如何提高自来…

企业级Windows设备日志采集工具

永久免费: 前往Gitee最新版本 更新内容 进一步提升工程师部署采集客户端效率. 打开根Url,自动跳转到部署页面.(原工程师需输入很长的Url);新增复制同类客户端同步任务功能.优化客户端分组操作;文件同步到服务器后,可配置文件名增加时间戳; 介绍 定时全量或增量采集工控机,…

字节码编程ASM之两数之和

写在前面 源码 。 看下如何使用ASM来写如下的类&#xff1a; package com.dahuyou.demo.asm;public class AsmSumOfTwo {public AsmSumOfTwo() {}public static void main(String[] var0) {int var1 (new AsmSumOfTwo()).sum(1, 2);System.out.println(var1);}public int su…

C++精解【8】

文章目录 运算,- 加减法* / 乘除法逐元 乘法逐元 除法逐元综合运算矩阵乘法与加减法 转置、共轭、伴随矩阵点乘法,叉积 运算 ,- 加减法 逐元加减法 #include <iostream> #include "e:/eigen/Eigen/Dense" using namespace std;int main() {Eigen::Matrix2d …

【源码】Spring Data JPA原理解析之Auditing执行原理

Spring Data JPA系列 1、SpringBoot集成JPA及基本使用 2、Spring Data JPA Criteria查询、部分字段查询 3、Spring Data JPA数据批量插入、批量更新真的用对了吗 4、Spring Data JPA的一对一、LazyInitializationException异常、一对多、多对多操作 5、Spring Data JPA自定…

vue + Lodop 制作可视化设计页面 实现打印设计功能(二)

历史&#xff1a; vue2 Lodop 制作可视化设计页面 实现打印设计功能&#xff08;一&#xff09; 前言&#xff1a; 之前本来打算用直接拿之前做的vue2版本改改就发的&#xff0c;但考虑到现在主流都是vue3了&#xff0c;所以从这篇文章开始使用vue3来写&#xff0c;以及最后…

Spring相关面试题(三)

29 如何在所有的BeanDefinition注册完成后&#xff0c;进行扩展 Bean工厂的后置处理器&#xff0c;在所有的Bean注册完成后&#xff0c;就被执行。 public class A implements BeanFactoryPostProcessor {private String name "a class";private B b; ​public St…

项目分层--简单图书管理系统

分层情况 实体类Book代码 //实体类 public class Book {private int id;private String name;private int bsum;public Book() {}public Book(int id, String name, int bsum) {this.id id;this.name name;this.bsum bsum;}public int getId() {return id;}public void set…

2024上海初中生古诗文大会倒计时4个多月:真题示例和独家解析

现在距离2024年初中生古诗文大会还有4个多月时间&#xff0c;我们继续来看10道选择题真题和详细解析&#xff0c;以下题目截取自我独家制作的在线真题集&#xff0c;都是来自于历届真题&#xff0c;去重、合并后&#xff0c;每道题都有参考答案和解析。 为帮助孩子自测和练习&…

Midjourney 平替 Leonardo AI 国内版上线关键还免费

Leonardo AI 正式在国内上线&#xff0c;功能相对基础&#xff0c;计划在两周后推出新一轮的更新&#xff0c;届时将支持 Elements (Lora) 和一些新的 XL 模型&#xff0c;逐步会把国际服上的功能移植过来。 虽然界面是英文&#xff0c;但是不要慌&#xff0c;你可以在 fanbook…

Cloud Serpent

Cloud Serpent 风蛇&#xff0c;刷厄运北很好用的&#xff0c;不记得早前好像就是50级副本神庙带俯冲&#xff0c;加速&#xff0c;远距离攻击&#xff0c;这样就不容易被厄运北法师和恶魔的法术攻击打中&#xff0c;残废术&#xff0c;而减速&#xff0c;刷本拉怪超级好用 闪…

历史的加速度:智人何时会迎来下一个版本?人类的命运与挑战

在《人类简史》中&#xff0c;尤瓦尔赫拉利主要探讨了人类的过去和发展历程&#xff0c;重点关注的是智人&#xff08;Homo sapiens&#xff09;。在他的续作《未来简史》中&#xff0c;他进一步探讨了未来人类的发展&#xff0c;并引入了“神人”&#xff08;Homo deus&#x…

C++之迭代器分类与List容器的使用

目录 迭代器的分类 List容器 ​编辑 总结 在Vector容器中我们学习了迭代器&#xff0c;知道了迭代器的作用和使用方法&#xff0c;本期我们将进一步学习迭代器的概念以及list容器的使用。 迭代器的分类 以算法库中的两个算法为例&#xff1a; sort算法是用来排序的&#…

常用MQ消息中间件Kafka、ZeroMQ和RabbitMQ对比及RabbitMQ详解

1、概述 在现代的分布式系统和实时数据处理领域&#xff0c;消息中间件扮演着关键的角色&#xff0c;用于解决应用程序之间的通信和数据传递的挑战。在众多的消息中间件解决方案中&#xff0c;Kafka、ZeroMQ和RabbitMQ 是备受关注和广泛应用的代表性系统。它们各自具有独特的特…

O2OA(翱途) 开发平台之HTTP端口规划

O2OA(翱途) 开发平台[下称O2OA开发平台或者O2OA]采用相对灵活的系统架构&#xff0c;支持三种服务器运行的方式。本篇主要阐述合并服务运行独立服务运行代理端口运行三种服务器运行方式。 一、先决条件&#xff1a; 1、O2Server服务器正常运行&#xff0c;系统安装部署请参考文…

Java基于jjwt操作jwt

之前讲解了jwt的相关知识&#xff0c;有不了解的&#xff0c;可以查看相关的文章JWT简介-CSDN博客&#xff0c;本节不再介绍&#xff0c;主要讲解有关java中如何通过jjwt库产生jwt以及解析jwt的相关操作。 添加maven依赖 <dependency><groupId>io.jsonwebtoken&l…

统信桌面操作系统上使用命令行添加软件图标到任务栏

原文链接&#xff1a;统信桌面操作系统上使用命令行添加软件图标到任务栏 Hello&#xff0c;大家好啊&#xff01;今天给大家带来一篇在统信桌面操作系统上使用命令行添加软件图标到任务栏的文章。通过命令行将常用软件的图标添加到任务栏&#xff0c;可以快速启动软件&#xf…

网络抓包分析工具

摘要 随着网络技术的快速发展&#xff0c;网络数据的传输和处理变得日益复杂。网络抓包分析工具作为网络故障排查、性能优化以及安全审计的重要工具&#xff0c;对于提升网络管理的效率和准确性具有重要意义。本文旨在设计并实现一款高效、易用的网络抓包分析工具&#xff0c;…

Python实现数据库与Excel文件之间的数据导入与导出

数据库和Excel文件是两种常见且重要的数据存储方式。数据库通常用于大规模数据的高效存储、管理和查询&#xff0c;而Excel则以其直观的界面和简单的操作方式广泛应用于数据分析、报告生成和可视化等领域。在实际工作中&#xff0c;可能需要在这两者之间进行数据的导入与导出。…