Spring Test 常见错误

前面我们介绍了许多 Spring 常用知识点上的常见应用错误。当然或许这些所谓的常用,你仍然没有使用,例如对于 Spring Data 的使用,,有的项目确实用不到。那么这一讲,我们聊聊 Spring Test,相信你肯定绕不开对它的使用,除非你不使用 Spring 来开发程序,或者你使用了 Spring 但是你不写测试。但话说回来,后者的情况就算你想如此,你的老板也不会同意吧。

那么在 Spring Test 的应用上,有哪些常见错误呢?这里我给你梳理了两个典型,闲话少叙,我们直接进入这一讲的学习。

案例 1:资源文件扫描不到

首先,我们来写一个 HelloWorld 版的 Spring Boot 程序以做测试备用。

先来定义一个 Controller:

@RestController
public class HelloController {

    @Autowired
    HelloWorldService helloWorldService;

    @RequestMapping(path = "hi", method = RequestMethod.GET)
    public String hi() throws Exception{
        return  helloWorldService.toString() ;
    };

}

当访问 http://localhost:8080/hi 时,上述接口会打印自动注入的 HelloWorldService 类型的 Bean。而对于这个 Bean 的定义,我们这里使用配置文件的方式进行。

1.定义 HelloWorldService,具体到 HelloWorldService 的实现并非本讲的重点,所以我们可以简单实现如下:

public class HelloWorldService {
}

2.定义一个 spring.xml,在这个 XML 中定义 HelloWorldServic 的 Bean,并把这个 spring.xml 文件放置在 /src/main/resources 中:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="helloWorldService" class="com.spring.puzzle.others.test.example1.HelloWorldService">
    </bean>
</beans>

3.定义一个 Configuration 引入上述定义 XML,具体实现方式如下:

@Configuration
@ImportResource(locations = {"spring.xml"})
public class Config {
}

完成上述步骤后,我们就可以使用 main() 启动起来。测试这个接口,一切符合预期。那么接下来,我们来写一个测试:

@SpringBootTest()
class ApplicationTests {

    @Autowired
    public HelloController helloController;

    @Test
    public void testController() throws Exception {
        String response = helloController.hi();
        Assert.notNull(response, "not null");
    }

}

 ​当我们运行上述测试的时候,会发现测试失败了,报错如下:

为什么单独运行应用程序没有问题,但是运行测试就不行了呢?我们需要研究一下 Spring 的源码,来找找答案。

案例解析

在了解这个问题的根本原因之前,我们先从调试的角度来对比下启动程序和测试加载 spring.xml 的不同之处。

1. 启动程序加载 spring.xml

首先看下调用栈:

 可以看出,它最终以 ClassPathResource 形式来加载,这个资源的情况如下:

 而具体到加载实现,它使用的是 ClassPathResource#getInputStream 来加载 spring.xml 文件:

从上述调用及代码实现,可以看出最终是可以加载成功的。

2. 测试加载 spring.xml

首先看下调用栈:

可以看出它是按 ServletContextResource 来加载的,这个资源的情况如下:

 具体到实现,它最终使用的是 MockServletContext#getResourceAsStream 来加载文件:

@Nullable
public InputStream getResourceAsStream(String path) {
    String resourceLocation = this.getResourceLocation(path);
    Resource resource = null;

    try {
        resource = this.resourceLoader.getResource(resourceLocation);
        return !resource.exists() ? null : resource.getInputStream();
    } catch (IOException | InvalidPathException var5) {
        if (this.logger.isWarnEnabled()) {
            this.logger.warn("Could not open InputStream for resource " + (resource != null ? resource : resourceLocation), var5);
        }

        return null;
    }
}

 ​你可以继续跟踪它的加载位置相关代码,即 getResourceLocation():

protected String getResourceLocation(String path) {
    if (!path.startsWith("/")) {
        path = "/" + path;
    }
    //加上前缀:/src/main/resources
    String resourceLocation = this.getResourceBasePathLocation(path);
    if (this.exists(resourceLocation)) {
        return resourceLocation;
    } else {
        //{"classpath:META-INF/resources", "classpath:resources", "classpath:static", "classpath:public"};
        String[] var3 = SPRING_BOOT_RESOURCE_LOCATIONS;
        int var4 = var3.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            String prefix = var3[var5];
            resourceLocation = prefix + path;
            if (this.exists(resourceLocation)) {
                return resourceLocation;
            }
        }

        return super.getResourceLocation(path);
    }
}

你会发现,它尝试从下面的一些位置进行加载:

classpath:META-INF/resources
classpath:resources
classpath:static
classpath:public
src/main/webapp

如果你仔细看这些目录,你还会发现,这些目录都没有 spring.xml。或许你认为源文件 src/main/resource 下面不是有一个 spring.xml 么?那上述位置中的 classpath:resources 不就能加载了么?

那你肯定是忽略了一点:当程序运行起来后,src/main/resource 下的文件最终是不带什么 resource 的。关于这点,你可以直接查看编译后的目录(本地编译后是 target\classes 目录),示例如下:

 所以,最终我们在所有的目录中都找不到 spring.xml,并且会报错提示加载不了文件。报错的地方位于 ServletContextResource#getInputStream 中:

问题修正

从上述案例解析中,我们了解到了报错的原因,那么如何修正这个问题?这里我们可以采用两种方式。

1. 在加载目录上放置 spring.xml

就本案例而言,加载目录有很多,所以修正方式也不少,我们可以建立一个 src/main/webapp,然后把 spring.xml 复制一份进去就可以了。也可以在 /src/main/resources 下面再建立一个 resources 目录,然后放置进去也可以。

2. 在 @ImportResource 使用 classpath 加载方式

@Configuration
//@ImportResource(locations = {"spring.xml"})
@ImportResource(locations = {"classpath:spring.xml"})
public class Config {
}

这里,我们可以通过 Spring 的官方文档简单了解下不同加载方式的区别,参考 https://docs.spring.io/spring-framework/docs/2.5.x/reference/resources.html:

很明显,我们一般都不会使用本案例的方式(即 locations = {"spring.xml"},无任何“前缀”的方式),毕竟它已经依赖于使用的 ApplicationContext。而 classPath 更为普适些,而一旦你按上述方式修正后,你会发现它加载的资源已经不再是 ServletContextResource,而是和应用程序一样的 ClassPathResource,这样自然可以加载到了。

所以说到底,表面上看,这个问题是关于测试的案例,但是实际上是 ImportResource 的使用问题。不过通过这个案例,你也会明白,很多用法真的只能在某个特定场合才能工作起来,你只是比较幸运而已。 

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

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

相关文章

网络编程,IO多路复用

1.使用IO多路复用完成TCP并发服务器 #include<myhead.h> #define SER_PORT 8888 //服务器端口号 #define SER_IP "192.168.124.10" //服务器IP地址int main(int argc, const char *argv[]) {//1、创建用于连接的套接字int sfd socket…

Elasticsearch从入门到精通-02环境搭建

Elasticsearch从入门到精通-02环境搭建 &#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是程序员行走的鱼 &#x1f342;博主从本篇正式开始ES学习&#xff0c;希望小伙伴可以一起探讨 &#x1f4d6; 本篇主要介绍和大家一块学习一下ES环境搭建,主要包括Elasticsearch、…

材料物理 (HIT) 笔记-2

原内容请参考哈尔滨工业大学何飞教授&#xff1a;https://www.bilibili.com/video/BV18b4y1Y7wd/?p12&spm_id_frompageDriver&vd_source61654d4a6e8d7941436149dd99026962 或《材料物理性能及其在材料研究中的应用》&#xff08;哈尔滨工业大学出版社&#xff09; 三…

mysql如何开启远程访问?

MySQL是一种常见的关系型数据库管理系统&#xff0c;广泛应用于各行各业。默认情况下&#xff0c;MySQL仅允许本地访问&#xff0c;即只能在本地主机上进行数据库操作。有时候我们需要通过远程连接访问MySQL数据库&#xff0c;以便实现更灵活的管理和操作。本文将介绍如何在MyS…

Docker快速入门和部署项目

1&#xff0c;Docker是一个&#xff0c;快速构建、运行、管理应用的工具 。 2&#xff0c;前面我们了解过在Linux操作系统的常见的命令以及如何在Linux中部署一个人单体的项目。感受如何呢&#xff1f;&#xff1f;&#xff1f; 命令太多了&#xff0c;记不住 软件安装包名字复…

java数据结构YZP专栏-----二叉树 平衡二叉树(AVL)

主文章&#xff08;数据结构的索引目录—进不去就说明我还没写完&#xff09;https://blog.csdn.net/grd_java/article/details/122377505 模拟数据结构的网站&#xff1a;https://www.cs.usfca.edu/~galles/visualization/Algorithms.html 源码(码云)&#xff1a;https://gi…

Python onnxruntime推理yolov5和yolov8(最简易版)

支持yolov5和yolov8双模型 其中ChtDeploy中代码如下&#xff1a; import onnxruntime import numpy as np import cv2class ChtDeploy():def __init__(self, img_path, onnx_path, iou_threshold0.45, conf_threshold0.3, detect_w640, detect_h 640):self.img cv2.imread(im…

【LeetCode每日一题】【BFS模版与例题】【二维数组】1293. 网格中的最短路径

BFS基本模版与案例可以参考&#xff1a; 【LeetCode每日一题】【BFS模版与例题】863.二叉树中所有距离为 K 的结点 【LeetCode每日一题】【BFS模版与例题】【二维数组】130被围绕的区域 && 994 腐烂的橘子 思路&#xff1a; 特殊情况&#xff1a; 最短的路径是向下再向…

多维时序 | Matlab实现GRNN广义回归神经网络多变量时间序列预测

文章目录 效果一览文章概述源码设计参考资料效果一览

SpringBoot(详细介绍)

目录 一、简介 1.1、什么是SpringBoot 1.2.、特性 1.3、四大核心 1.4、特点 二、快速入门 2.1、开发流程 2.1.1、创建项目 maven项目 2.1.2、导入场景 场景启动器 2.1.3、主程序 2.1.4、业务 三、Spring Initializr 创建向导 3.1、依赖管理机制 3.1.1、为什么导…

JRebel and XRebel 插件在IDEA中的安装、激活和使用

1、JRebel安装 1、打开idea->setting->plugins->Marketplace 2、搜索插件JRebel and XRebel&#xff0c;点击安装&#xff0c;然后重启idea 如果左侧出现JRebel & XRebel代表已安装 3.离线安装JRebel 根据自己安装的idea版本进行下载电影的jrebel https://plugi…

H5小游戏,象棋

H5小游戏源码、JS开发网页小游戏开源源码大合集。无需运行环境,解压后浏览器直接打开。有需要的,私信本人,发演示地址,可以后再订阅,发源码,含60+小游戏源码。如五子棋、象棋、植物大战僵尸、开心消消乐、扑鱼达人、飞机大战等等 <!DOCTYPE html PUBLIC "-//W3C/…

spring学习简单总结(尚硅谷视频

spring学习简单总结 spring-tx-事务注解添加用注解方式进行aop的配置基于注解配置类进行ioc和di的配置 spring-tx-事务注解添加 选择对应事务管理器实现加入到ioc容器 使用注解指定哪些方法需要添加事务 用注解指定方法 用注解方式进行aop的配置 写自己正常的核心业务代…

Python学习 day07(JSON、format()函数)

JSON 各种编程语言存储数据的容器不尽相同&#xff0c;在Python中有字典dict这样的数据类型&#xff0c;而其他语言可能没有对应的字典&#xff0c;为了让不同的语言都能够相互通用的传递数据&#xff0c;JSON就是一种非常良好的中转数据格式&#xff0c;如下&#xff1a; JSON…

前端工具网站合集(持续更新)

综合类网站 那些免费的砖 统计推荐免费工具网站 那些免费的砖 - 优雅地白嫖各种免费资源 (thosefree.com)https://www.thosefree.com/ CSS样式网站 毒蘑菇-配色 CSS 配色&#xff0c;阴影网站 一个好用的配色网站! 毒蘑菇 - 配色 (dumogu.top)https://color.dumogu.top/ …

navicat想把自己库中的表导出给别人的操作

navicat导出表和导入表 导出 右键需要导出的表&#xff0c;选择导出向导 选择csv&#xff0c;然后点击下一步 到这个页面&#xff0c;其实可以不用选择&#xff0c;会自己选择当时选中的表&#xff0c;如果有多张表导出&#xff0c;可以选择其他表。然后点击下一步 到这里是选…

【Django】执行查询——查询JSONField

JSONField 本篇的例子以下面这个模型为基础&#xff1a; from django.db import modelsclass Dog(models.Model):name models.CharField(max_length200)data models.JSONField(nullTrue)def __str__(self):return self.name保存和查询None值 在使用JSONField时&#xff0c…

springboot+jsp汽车配件管理系统idea maven 项目lw

springbootweb汽车配件销售业绩管理系统服务于汽车配件公司业务,实现了客户管理&#xff0c;主要负责对客户相关数据的增删改查方面、渠道管理&#xff0c;主要对渠道信息也就是设备的供应商渠道信息进行管理、项目管理&#xff0c;主要是一些项目信息的记录与整理、销售数据管…

【Photoshop2020版本】零基础笔记(一)

哈喽大家好~最近博客内容换方向了哈哈哈~换成“实用版”了。 今天给大家带来的是 PS 相关内容 其实我也是刚学PS&#xff0c;所以想着自己做笔记还不如发布出去&#xff0c;让大家都能看到&#xff0c;有兴趣的伙伴们&#xff0c;可以跟着我的笔记一块学习&#xff0c;这个专…

Python-Numpy-计算矩阵相乘

向量化矩阵计算公式&#xff1a; """ Title: matrix_calculating Time: 2024/3/6 Author: Michael Jie """import numpy as npw np.array([[1, 2]]) x np.array([[1, 1], [2, 3], [4, 5]]) b 1# w * x b print(w * x b) """…