我有一个Web应用程序,它将Hibernate Integrator定义为Java ServiceLoader规范的一部分,如下所示:
的src /主/资源/ META-INF /服务/ org.hibernate.integrator.spi.Integrator
# Define integrators that should be instantiated by the ServiceLoader
org.emmerich.MyIntegrator
这是根据Hibernate指南here完成的。
我的问题是,当我尝试执行单元测试时,主Integrator描述符仍然被解析和实例化。这意味着,因为我在单元测试中嘲笑应用程序的很大一部分,当集成商试图运行时遇到错误导致我的测试失败。
我在测试资源中定义了相同的文件:
的src /测试/资源/ META-INF /服务/ org.hibernate.integrator.spi.Integrator
# Empty file to try and overwrite the main deployment description.
但我发现解析了测试和主要集成器文件。
我预计测试资源会覆盖主资源,从而使主要资源成为可能,但事实并非如此。因为这两个文件都在类路径上(我使用surefire-plugin通过Maven运行测试,它将test-classes
和classes
放在类路径上。类似的事情发生在persistence.xml
。
在我的单元测试环境中,我不希望实例化任何Integrator,因为我想尽可能手动控制这些bean的构造。假设我正在测试执行单元,我不希望其他bean(例如积分器)可能会影响测试的运行。我认为这是单元测试期间完全合理的要求。但是,虽然主要资源仍由ServiceLoader
解析,但这是不可能的。
我要做的解决方案是基于此处发布的persistence.xml
解决方案:
How to configure JPA for testing in Maven
我的问题是,在单元测试期间是否有更好的方法可以排除主要资源,而不是强制重命名,尤其是在ServiceLoader
文件的上下文中?
尝试总结一下:
如果两个文件都以类路径上的相同服务接口命名,会发生什么?对我来说,似乎两个文件中的所有服务都是实例化的。看起来没有覆盖。
答案 0 :(得分:1)
对于那些感兴趣的人,进行一些调查会导致这不是真正的Hibernate问题,而是ServiceLoader
加载文件的方式。来自API:
如果在多个中指定了特定的具体提供程序类 配置文件,或者在同一配置文件中命名的更多 不止一次,然后忽略副本。
不幸的是,这仅适用于具体类。因此,如果我在表单中指定同一文件的两个副本:
<强> org.emmerich.MyServiceInterface 强>
org.emmerich.MyServiceImpl
然后MyServiceImpl
仅实例化一次。但是,如果我指定两个具有两个不同实现类的文件,则实例化两个实现。
对于您希望对服务实例化位置进行更精细控制的单元测试,这不是很好。我对它背后的设计决策并不是很了解,但是Hibernate对它的使用使得单元测试变得更加困难。
无论如何,我提出的解决方案是将主要资源的覆盖委托给我控制的文件,例如properties
文件。在服务中,我现在检查该属性文件中的标志是真还是假。在我的主要资源中,这是真的。在我的测试中,这是错误的。因为查找属性会触及类路径上的第一个文件,所以我知道它将获得测试资源。我的结构看起来像这样:
src
main
resources
META-INF
services
org.hibernate.integrator.spi.Integrator # has line MyIntegrator
application.properties # shouldIntegrate=true
test
resources
application.properties # shouldIntegrate=false
在MyIntegrator
:
public class MyIntegrator implements Integrator {
@Override
public void integrate(Configuration configuration, SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
if(shouldIntegrate()) {
// do integration
}
}
private boolean shouldIntegrate() {
// read Properties file from classpath
// getClass().getClassLoader().getResourceAsStream("application.properties")
// return value of "shouldIntegrate"
}
当我在测试环境中运行时,属性文件查找指向测试资源。在测试环境之外,它指向主要资源。