Java的ServiceLoader和测试资源

EMM*_*ICH 5 java service hibernate jpa serviceloader

我有一个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
Run Code Online (Sandbox Code Playgroud)

这是根据Hibernate的指导做这里.

我的问题是,当我尝试执行单元测试时,主Integrator描述符仍然被解析和实例化.这意味着,因为我在单元测试中嘲笑应用程序的很大一部分,当集成器试图运行时遇到错误导致我的测试失败.

我在测试资源中定义了相同的文件:

SRC /测试/资源/ META-INF /服务/ org.hibernate.integrator.spi.Integrator

  # Empty file to try and overwrite the main deployment description.
Run Code Online (Sandbox Code Playgroud)

但相反,我发现解析了测试和主要集成商文件.

我预计测试资源会覆盖主资源,从而使主要资源成为可能,但事实并非如此.由于这两个文件是在类路径中(我基于Maven与神火-插件,它可以让这运行测试test-classes,并classes在classpath).类似的事情发生在persistence.xml.

在我的单元测试环境中,我不希望实例化任何Integrator,因为我想尽可能手动控制这些bean的构造.假设我正在测试执行单元,我不希望其他bean(例如积分器)可能会影响测试的运行.我认为这是单元测试期间完全合理的要求.但是,虽然主要资源仍由解析ServiceLoader,但这是不可能的.

我要做的persistence.xml解决方案是基于此处发布的解决方案:

如何配置JPA以在Maven中进行测试

我的问题是,在单元测试中是否有更好的方法可以排除主要资源的处理,而不是强制重命名,尤其是在ServiceLoader文件的上下文中?

试着总结一下:

当两个文件都以类路径上的相同服务接口命名时会发生什么?对我来说,似乎两个文件中的所有服务都是实例化的.看起来没有覆盖.

EMM*_*ICH 1

对于那些感兴趣的人来说,多一点调查就会发现,这实际上并不是 Hibernate 的问题,而更多的是ServiceLoader文件加载方式的问题。从API

如果某一特定的具体提供程序类在多个配置文件中命名,或者在同一配置文件中多次命名,则重复项将被忽略。

不幸的是,这只适用于具体的类。因此,如果我以以下形式指定同一文件的两个副本:

org.emmerich.MyServiceInterface

  org.emmerich.MyServiceImpl
Run Code Online (Sandbox Code Playgroud)

thenMyServiceImpl只实例化一次。但是,如果我指定具有两个不同实现类的两个文件,则两个实现都会被实例化。

这对于单元测试来说并不是很好,因为您需要对服务实例化的位置进行更精细的控制。我不太了解其背后的设计决策,但 Hibernate 对此的使用使得单元测试变得更加困难。

无论如何,我想出的解决方案是将主要资源的重写委托给我可以控制的文件,例如文件properties。在服务内部,我现在检查该属性文件中的标志是 true 还是 false。在我的主要资源中,这是真的。在我的测试中,这是错误的。因为查找属性会命中类路径上的第一个文件,所以我知道它将获取测试资源。我的结构如下所示:

src
  main
    resources
      META-INF
        services
          org.hibernate.integrator.spi.Integrator # has line MyIntegrator
    application.properties     # shouldIntegrate=true 
  test
    resources
      application.properties   # shouldIntegrate=false
Run Code Online (Sandbox Code Playgroud)

并在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"
  }
Run Code Online (Sandbox Code Playgroud)

当我在测试环境中运行时,属性文件查找指向测试资源。在测试环境之外,它指向主要资源。