Java ServiceLoader and Test Resources

I have a web application that defines Hibernate Integrator as part of the Java ServiceLoader specification, for example:

Src / main / resources / META-INF / services / org.hibernate.integrator.spi.Integrator

# Define integrators that should be instantiated by the ServiceLoader org.emmerich.MyIntegrator 

This is done according to the Hibernate guide here .

My problem is that when I try to run unit tests, the main Integrator descriptor is still parsed and created. This means that because I mock most of the application in unit tests, when the integrator tries to run it, it encounters errors that make my tests fail.

I defined the same file in test resources:

Src / test / resources / META-INF / services / org.hibernate.integrator.spi.Integrator

  # Empty file to try and overwrite the main deployment description. 

but instead, I find that the analytic and core files of the integrator are being analyzed.

I expected the test resource to overwrite the main resource, thereby providing the main obscolete resource, but this is not what happens. Since both files are in the class path (I run tests through Maven using a valid plugin that puts both test-classes and classes in the class path). The same thing happens with persistence.xml .

In my modular test environment, I do not want any integrators to be created, because I want to manage the construction of these beans as quickly as possible. Let me test the units of execution, I don’t want additional beans, such as integrators lying around, to affect the operation of the tests. I think this is an absolutely legal requirement during unit testing. However, while the main resources are still being processed by ServiceLoader , this is not possible.

The solution I came up with is based on the persistence.xml solution posted here:

How to configure JPA for testing in Maven

My question is, is there a better way to exclude core processing resources during unit testing than force renaming, especially in the context of ServiceLoader files?

Try and summarize it a little better:

What happens if you have two files, both of which are named after the same service interface in the classpath? It seems to me that all services in both files are created. There seems to be no rewriting.

+4
source share
1 answer

For those who are interested, a little more research leads to the fact that this is not a Hibernate problem, but more than ServiceLoader loading in files. From the API :

If a particular concrete provider class is named more than one configuration file or is specified more than once in the same configuration file, then duplicates are ignored.

Unfortunately, this applies only to specific classes. Therefore, if I specify two copies of the same file in the form:

org.emmerich.MyServiceInterface

  org.emmerich.MyServiceImpl 

then MyServiceImpl is created only once. However, if I specify two files with two different implementation classes, then both implementations will be implemented.

This is not well suited for unit testing, where you need a little finer control over where your services are created. I don’t know enough about the design solutions behind it, but using Hibernate in this makes unit test difficult.

In any case, the solution I came across was to delegate the redefinition of core resources to a file that I controlled, such as a properties file. Inside the service, I now check if the flag is true or false in this properties file. In my main resource this is true. This is not true in my test. Since the property search falls into the first file in the classpath, I know that it will receive a test resource. My structure looks like this:

 src main resources META-INF services org.hibernate.integrator.spi.Integrator # has line MyIntegrator application.properties # shouldIntegrate=true test resources application.properties # shouldIntegrate=false 

And in 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" } 

When I start the test environment, the search for the properties file points to the test resource. Outside the test environment, it points to the main resource.

+1
source

All Articles