问题

本地开发项目时,直接将项目部署到idea配置的tomcat中能正常启动,但是使用Spring的Junit测试时,启动报错class path resource [xxx.xml] cannot be opened because it does not exist。测试代码如下:

//@WebAppConfiguration
@RunWith(SpringJUnit4ClassRunner.class) // RunWith注解表示JUnit将不会跑其内置的测试,而是运行所引用的类中的所有测试
@ContextConfiguration(locations={"classpath:AppContext-spring.xml","classpath:AppContext-shiro.xml"/*,"classpath:AppContext-mvc.xml"*/}) //此注解用来加载配置ApplicationContext
@Slf4j
public class FunctionTest extends BaseUnit {
	@Autowired
    ManualDealMapper manualDealMapper;
    
    @Test
    public void test_selectVideo() {
        PolicyInfo2 info2 = new PolicyInfo2();
        info2.setIsManualDealt("0");
        List<PolicyInfo2> list = manualDealMapper.selectVideo(info2, null, null, "asc");
        System.out.println("list.size() = " + list.size());
    }
}

注意:此时代码中没有开启@WebAppConfiguration
注意:遇到这种问题,最好先把项目的编译输出目录删掉,然后点击Gradle工具窗中的“Reload All Gradle Projects”按钮重新构建项目(而不是工具类的 build -> build project),因为有时重新构建一下就好,就是这么莫名其妙。

分析

在测试类构造方法上获取当前测试环境的classpath路径(注:不知道为啥一段时间后再一次这样做,得到的值变成了E:/xx/out/product/,也就没有问题了)
在这里插入图片描述
其实也就是我们在Gradle或者Idea中配置的路径:
在这里插入图片描述
然后去这个classpath路径看一下是不是真的没有这个xml配置文件,发现确实没有。说明确实是我们的配置文件没有被成功编译输出到classpath中。
在这里插入图片描述
最后发现原因是test模块中只有java包,缺少了resources包,而main模块中的resources包是不会被test模块编译的。
在这里插入图片描述

解决

方式一:手动处理test模块的编译输出目录

在每次进行junit测试之前手动将main模块编译输出目录中的resources包里的所有文件复制到test模块的编译输出目录(/out/test/classes)下,这样就能使得测试时classpath路径下有xml配置文件。
在这里插入图片描述

方式二:为test模块添加resources包(推荐)

可以将main模块中的resources包复制到test模块中,注意看看复制过来的resources包上是否标有“Test Resources Root”的小图标,若没有就选中它 --> 右键 --> “Mark Directory As ” --> “Test Resources Root”,这样的目录结构是符合maven-web项目的标准目录结构。然后点击操作栏的"build" --> “Build Project”,就能看到xml配置文件被编译输出到classpath路径了。如下图:
在这里插入图片描述

其实也可以不为test模块添加resources包,而是每次junit测试之前手动将main模块编译输出目录中的resources包复制到/out/test/目录下,即将/out/test目录结构手动改为跟上图一样。

方式二还有问题

但是再次启动测试还是报这个错,不过其实如果你细心的话,应该早就发现了是因为classpath是/out/test/classes目录,而我们的xml配置文件在/out/test/resources目录中(原因见下面引用),类加载器当然找不到它们啦,所以我们要做的是让他们也在classpath中!

maven/gradle 的plugin-war默认会从 src/main/java 搜寻打包源码,在 src/test/java 下搜寻测试源码。并且 src/main/resources 下的所有文件按都会被打包,所有 src/test/resources 下的文件 都会被添加到类路径用以执行测试。所有文件都输出到 build 下,打包的文件输出到 build/libs 下。

maven/gradle的plugin-war将项目目录结构定义的有别于程序包目录结构,是为了方便程序员在编写程序的时候更加直观的管理项目文件,因为程序包定义的目录结构不一定适合人们直观的对其进行操作。比如war包专门定义了一个Resource目录,方便用户集中管理资源文件和各种配置文件。

再解决方式二的问题

使得xml配置文件编译输出到/out/test/classes目录下,参考https://blog.csdn.net/sinat_38301574/article/details/80465693

后来"最开始的场景"又莫名其妙的不报错了

再次 重新整理。。。

每次改project或moudle设置后,都先删除out目录,再点击Gradle工具窗中的“Reload All Gradle Projects”按钮方式来构建一下项目,再进行测试;

test/resources包 加载mvc.xml testCompile(org.glassfish.web:javax.el) @WebAppConfiguration 启动情况(若正常则记录@ContextConfiguration的类classLoader路径) 分析
× × × × E:/xx/out/production/resources/ 不使用mvc.xml中注册的bean时用这种方式
× × × HV000183:Unable to load ‘javax.el.ExpressionFactory’ 因为我的mvc.xml配置了扫描有@EnableAutoConfiguration的类,自动配置会用到org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor,这个类会使用hibernate-validator,而validator依赖javax.el,servlet容器(tomcat)自带有实现,而junit测试没有
× × No qualifying bean of type [javax.servlet.http.HttpServletRequest] found for dependency 因为我的BaseController类中需要注入HttpServletRequest,Servlet容器(tomcat)是有HttpServletRequest和HttpServletResponse的实现的,而junit测试没有
× E:/xx/out/production/resources/ 要使用mvc.xml中注册的bean时用这种方式
Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐