HHH-15344 - Ability to apply testing annotations at method-level

- `@ServiceRegistry`
    - `@BootstrapServiceRegistry`
This commit is contained in:
Steve Ebersole 2022-06-16 09:33:04 -05:00
parent 763d1764cd
commit edc95df72a
5 changed files with 235 additions and 101 deletions

View File

@ -45,8 +45,6 @@ public interface Integrator {
* @param metadata The fully initialized boot-time mapping model * @param metadata The fully initialized boot-time mapping model
* @param bootstrapContext The context for bootstrapping of the SessionFactory * @param bootstrapContext The context for bootstrapping of the SessionFactory
* @param sessionFactory The SessionFactory being created * @param sessionFactory The SessionFactory being created
*
* todo (6.0) : why pass the `serviceRegistry`? Why not just grab it from the SessionFactory?
*/ */
@Incubating @Incubating
default void integrate( default void integrate(

View File

@ -0,0 +1,4 @@
The `hibernate-testing` module of Hibernate ORM defines utilities for writing
tests easier....
// todo : write more (duh)

View File

@ -99,7 +99,7 @@ public class DomainModelExtension
} }
private static ExtensionContext.Store locateExtensionStore(Object testInstance, ExtensionContext context) { private static ExtensionContext.Store locateExtensionStore(Object testInstance, ExtensionContext context) {
return JUnitHelper.locateExtensionStore( ServiceRegistryExtension.class, context, testInstance ); return JUnitHelper.locateExtensionStore( DomainModelExtension.class, context, testInstance );
} }
private static DomainModelScope createDomainModelScope( private static DomainModelScope createDomainModelScope(
@ -199,27 +199,13 @@ public class DomainModelExtension
} }
public static DomainModelScope findDomainModelScope(Object testInstance, ExtensionContext context) { public static DomainModelScope findDomainModelScope(Object testInstance, ExtensionContext context) {
// todo : allow for method-level
final ExtensionContext.Store store = locateExtensionStore( testInstance, context ); final ExtensionContext.Store store = locateExtensionStore( testInstance, context );
final DomainModelScope existing = (DomainModelScope) store.get( MODEL_KEY ); final DomainModelScope existing = (DomainModelScope) store.get( MODEL_KEY );
if ( existing != null ) { if ( existing != null ) {
return existing; return existing;
} }
final Optional<DomainModel> domainModelAnnRef = AnnotationSupport.findAnnotation( throw new RuntimeException( "Could not locate @DomainModel annotation : " + context.getDisplayName() );
context.getElement().get(),
DomainModel.class
);
if ( domainModelAnnRef.isEmpty() ) {
throw new RuntimeException( "Could not locate @DomainModel annotation : " + context.getDisplayName() );
}
final DomainModelScope created = createDomainModelScope( testInstance, domainModelAnnRef, context );
locateExtensionStore( testInstance, context ).put( MODEL_KEY, created );
return created;
} }
protected static void applyCacheSettings(Metadata metadata, boolean overrideCacheStrategy, String cacheConcurrencyStrategy) { protected static void applyCacheSettings(Metadata metadata, boolean overrideCacheStrategy, String cacheConcurrencyStrategy) {

View File

@ -22,7 +22,7 @@ import org.hibernate.service.spi.ServiceContributor;
import org.hibernate.testing.boot.ExtraJavaServicesClassLoaderService; import org.hibernate.testing.boot.ExtraJavaServicesClassLoaderService;
import org.hibernate.testing.boot.ExtraJavaServicesClassLoaderService.JavaServiceDescriptor; import org.hibernate.testing.boot.ExtraJavaServicesClassLoaderService.JavaServiceDescriptor;
import org.junit.jupiter.api.extension.AfterAllCallback; import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.TestExecutionExceptionHandler; import org.junit.jupiter.api.extension.TestExecutionExceptionHandler;
import org.junit.jupiter.api.extension.TestInstancePostProcessor; import org.junit.jupiter.api.extension.TestInstancePostProcessor;
@ -37,14 +37,84 @@ import org.jboss.logging.Logger;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class ServiceRegistryExtension public class ServiceRegistryExtension
implements TestInstancePostProcessor, AfterAllCallback, TestExecutionExceptionHandler { implements TestInstancePostProcessor, BeforeEachCallback, TestExecutionExceptionHandler {
private static final Logger log = Logger.getLogger( ServiceRegistryExtension.class ); private static final Logger log = Logger.getLogger( ServiceRegistryExtension.class );
private static final String REGISTRY_KEY = ServiceRegistryScope.class.getName(); private static final String REGISTRY_KEY = ServiceRegistryScope.class.getName();
public static StandardServiceRegistry findServiceRegistry( @Override
Object testInstance, public void postProcessTestInstance(Object testInstance, ExtensionContext context) {
ExtensionContext context) { log.tracef( "#postProcessTestInstance(%s, %s)", testInstance, context.getDisplayName() );
return findServiceRegistryScope( testInstance, context ).getRegistry();
assert context.getTestClass().isPresent();
final Optional<BootstrapServiceRegistry> bsrAnnRef = AnnotationSupport.findAnnotation(
context.getElement().get(),
BootstrapServiceRegistry.class
);
final Optional<ServiceRegistry> ssrAnnRef = AnnotationSupport.findAnnotation(
context.getElement().get(),
ServiceRegistry.class
);
final ServiceRegistryScope created = createServiceRegistryScope( testInstance, bsrAnnRef, ssrAnnRef, context );
locateExtensionStore( testInstance, context ).put( REGISTRY_KEY, created );
}
@Override
public void beforeEach(ExtensionContext context) throws Exception {
assert context.getTestMethod().isPresent();
assert context.getRequiredTestMethod() == context.getElement().get();
Optional<BootstrapServiceRegistry> bsrAnnRef = AnnotationSupport.findAnnotation(
context.getElement().get(),
BootstrapServiceRegistry.class
);
Optional<ServiceRegistry> ssrAnnRef = AnnotationSupport.findAnnotation(
context.getElement().get(),
ServiceRegistry.class
);
if ( bsrAnnRef.isEmpty() && ssrAnnRef.isEmpty() ) {
// assume the annotations are defined on the class-level...
// will be validated by the parameter-resolver or consuming extension
return;
}
else if ( bsrAnnRef.isPresent() && ssrAnnRef.isPresent() ) {
// the method has both - use them -> fall through
}
else if ( bsrAnnRef.isPresent() ) {
// the method has BootstrapServiceRegistry but not ServiceRegistry
//
// see if there is a ServiceRegistry at the class-level:
// yes -> use this class-level one
// no -> treat it as implicit
ssrAnnRef = AnnotationSupport.findAnnotation(
context.getRequiredTestClass(),
ServiceRegistry.class
);
}
else if ( ssrAnnRef.isPresent() ) {
// the method has ServiceRegistry but not BootstrapServiceRegistry
//
// see if there is a BootstrapServiceRegistry at the class-level:
// yes -> use this class-level one
// no -> treat it as implicit
bsrAnnRef = AnnotationSupport.findAnnotation(
context.getRequiredTestClass(),
BootstrapServiceRegistry.class
);
}
else {
throw new RuntimeException( "Some clever text" );
}
final Object testInstance = context.getRequiredTestInstance();
final ServiceRegistryScope created = createServiceRegistryScope( testInstance, bsrAnnRef, ssrAnnRef, context );
final ExtensionContext.Store extensionStore = locateExtensionStore( testInstance, context );
extensionStore.put( REGISTRY_KEY, created );
} }
private static ExtensionContext.Store locateExtensionStore( private static ExtensionContext.Store locateExtensionStore(
@ -57,62 +127,62 @@ public class ServiceRegistryExtension
log.tracef( "#findServiceRegistryScope(%s, %s)", testInstance, context.getDisplayName() ); log.tracef( "#findServiceRegistryScope(%s, %s)", testInstance, context.getDisplayName() );
final ExtensionContext.Store store = locateExtensionStore( testInstance, context ); final ExtensionContext.Store store = locateExtensionStore( testInstance, context );
final ServiceRegistryScopeImpl existingScope = (ServiceRegistryScopeImpl) store.get( REGISTRY_KEY );
ServiceRegistryScopeImpl existingScope = (ServiceRegistryScopeImpl) store.get( REGISTRY_KEY );
if ( existingScope == null ) { if ( existingScope == null ) {
log.debugf( "Creating ServiceRegistryScope - %s", context.getDisplayName() ); throw new RuntimeException( "No ServiceRegistryScope known in context" );
final BootstrapServiceRegistryProducer bsrProducer;
final Optional<BootstrapServiceRegistry> bsrAnnWrapper = AnnotationSupport.findAnnotation(
context.getElement().get(),
BootstrapServiceRegistry.class
);
if ( bsrAnnWrapper.isPresent() ) {
bsrProducer = bsrBuilder -> {
final BootstrapServiceRegistry bsrAnn = bsrAnnWrapper.get();
configureJavaServices( bsrAnn, bsrBuilder );
configureIntegrators( bsrAnn, bsrBuilder );
return bsrBuilder.enableAutoClose().build();
};
}
else {
bsrProducer = BootstrapServiceRegistryBuilder::build;
}
final ServiceRegistryProducer ssrProducer;
if ( testInstance instanceof ServiceRegistryProducer ) {
ssrProducer = (ServiceRegistryProducer) testInstance;
}
else {
ssrProducer = new ServiceRegistryProducerImpl(context);
}
final ServiceRegistryScopeImpl scope = new ServiceRegistryScopeImpl( bsrProducer, ssrProducer );
scope.getRegistry();
locateExtensionStore( testInstance, context ).put( REGISTRY_KEY, scope );
if ( testInstance instanceof ServiceRegistryScopeAware ) {
( (ServiceRegistryScopeAware) testInstance ).injectServiceRegistryScope( scope );
}
return scope;
} }
return existingScope; return existingScope;
} }
private static ServiceRegistryScopeImpl createServiceRegistryScope(
Object testInstance,
Optional<BootstrapServiceRegistry> bsrAnnRef,
Optional<ServiceRegistry> ssrAnnRef,
ExtensionContext context) {
log.debugf( "Creating ServiceRegistryScope - %s", context.getDisplayName() );
final BootstrapServiceRegistryProducer bsrProducer;
//noinspection OptionalIsPresent
if ( bsrAnnRef.isPresent() ) {
bsrProducer = bsrBuilder -> {
final BootstrapServiceRegistry bsrAnn = bsrAnnRef.get();
configureJavaServices( bsrAnn, bsrBuilder );
configureIntegrators( bsrAnn, bsrBuilder );
return bsrBuilder.enableAutoClose().build();
};
}
else {
bsrProducer = BootstrapServiceRegistryBuilder::build;
}
final ServiceRegistryProducer ssrProducer;
if ( testInstance instanceof ServiceRegistryProducer ) {
ssrProducer = (ServiceRegistryProducer) testInstance;
}
else {
ssrProducer = new ServiceRegistryProducerImpl( ssrAnnRef );
}
final ServiceRegistryScopeImpl scope = new ServiceRegistryScopeImpl( bsrProducer, ssrProducer );
scope.getRegistry();
if ( testInstance instanceof ServiceRegistryScopeAware ) {
( (ServiceRegistryScopeAware) testInstance ).injectServiceRegistryScope( scope );
}
return scope;
}
private static class ServiceRegistryProducerImpl implements ServiceRegistryProducer{ private static class ServiceRegistryProducerImpl implements ServiceRegistryProducer{
private final ExtensionContext context; private final Optional<ServiceRegistry> ssrAnnRef;
public ServiceRegistryProducerImpl(ExtensionContext context) {
this.context = context; public ServiceRegistryProducerImpl(Optional<ServiceRegistry> ssrAnnRef) {
if ( !context.getElement().isPresent() ) { this.ssrAnnRef = ssrAnnRef;
throw new RuntimeException( "Unable to determine how to handle given ExtensionContext : " + context.getDisplayName() );
}
} }
@Override @Override
@ -120,13 +190,8 @@ public class ServiceRegistryExtension
// set some baseline test settings // set some baseline test settings
ssrb.applySetting( AvailableSettings.STATEMENT_INSPECTOR, org.hibernate.testing.jdbc.SQLStatementInspector.class ); ssrb.applySetting( AvailableSettings.STATEMENT_INSPECTOR, org.hibernate.testing.jdbc.SQLStatementInspector.class );
final Optional<ServiceRegistry> ssrAnnWrapper = AnnotationSupport.findAnnotation( if ( ssrAnnRef.isPresent() ) {
context.getElement().get(), final ServiceRegistry serviceRegistryAnn = ssrAnnRef.get();
ServiceRegistry.class
);
if ( ssrAnnWrapper.isPresent() ) {
final ServiceRegistry serviceRegistryAnn = ssrAnnWrapper.get();
configureServices( serviceRegistryAnn, ssrb ); configureServices( serviceRegistryAnn, ssrb );
} }
@ -218,28 +283,22 @@ public class ServiceRegistryExtension
} }
} }
@Override // @Override
public void postProcessTestInstance(Object testInstance, ExtensionContext context) { // public void afterAll(ExtensionContext context) {
log.tracef( "#postProcessTestInstance(%s, %s)", testInstance, context.getDisplayName() ); // log.tracef( "#afterAll(%s)", context.getDisplayName() );
findServiceRegistryScope( testInstance, context ); //
} // final Object testInstance = context.getRequiredTestInstance();
//
@Override // if ( testInstance instanceof ServiceRegistryScopeAware ) {
public void afterAll(ExtensionContext context) { // ( (ServiceRegistryScopeAware) testInstance ).injectServiceRegistryScope( null );
log.tracef( "#afterAll(%s)", context.getDisplayName() ); // }
//
final Object testInstance = context.getRequiredTestInstance(); // final ExtensionContext.Store store = locateExtensionStore( testInstance, context );
// final ServiceRegistryScopeImpl scope = (ServiceRegistryScopeImpl) store.remove( REGISTRY_KEY );
if ( testInstance instanceof ServiceRegistryScopeAware ) { // if ( scope != null ) {
( (ServiceRegistryScopeAware) testInstance ).injectServiceRegistryScope( null ); // scope.close();
} // }
// }
final ExtensionContext.Store store = locateExtensionStore( testInstance, context );
final ServiceRegistryScopeImpl scope = (ServiceRegistryScopeImpl) store.remove( REGISTRY_KEY );
if ( scope != null ) {
scope.close();
}
}
@Override @Override
public void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable { public void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable {

View File

@ -0,0 +1,87 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
package org.hibernate.testing.annotations.methods;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.service.spi.ServiceContributor;
import org.hibernate.testing.orm.junit.BootstrapServiceRegistry;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.ServiceRegistryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Steve Ebersole
*/
@BootstrapServiceRegistry(javaServices = @BootstrapServiceRegistry.JavaService(
role = ServiceContributor.class,
impl = ServiceRegistryTesting.ServiceContributor1.class
))
@ServiceRegistry( settings = @Setting( name = "setting", value = "class-level"))
public class ServiceRegistryTesting {
@Test
public void testClassLevel(ServiceRegistryScope scope) {
scope.withService( ConfigurationService.class, (configurationService) -> {
assertThat( configurationService.getSettings().get( "setting" ) ).isEqualTo( "class-level" );
assertThat( configurationService.getSettings().get( "contributed" ) ).isEqualTo( "contributed-1" );
} );
}
@Test
@BootstrapServiceRegistry(javaServices = @BootstrapServiceRegistry.JavaService(
role = ServiceContributor.class,
impl = ServiceRegistryTesting.ServiceContributor2.class
))
@ServiceRegistry( settings = @Setting( name = "setting", value = "method-level"))
public void testMethodLevel(ServiceRegistryScope scope) {
scope.withService( ConfigurationService.class, (configurationService) -> {
assertThat( configurationService.getSettings().get( "setting" ) ).isEqualTo( "method-level" );
assertThat( configurationService.getSettings().get( "contributed" ) ).isEqualTo( "contributed-2" );
} );
}
@Test
@ServiceRegistry( settings = @Setting( name = "setting", value = "mixed"))
public void testMethodLevelImplicitBootstrap(ServiceRegistryScope scope) {
scope.withService( ConfigurationService.class, (configurationService) -> {
assertThat( configurationService.getSettings().get( "setting" ) ).isEqualTo( "mixed" );
assertThat( configurationService.getSettings().get( "contributed" ) ).isEqualTo( "contributed-1" );
} );
}
@Test
@BootstrapServiceRegistry(javaServices = @BootstrapServiceRegistry.JavaService(
role = ServiceContributor.class,
impl = ServiceRegistryTesting.ServiceContributor2.class
))
public void testMethodLevelImplicitStandard(ServiceRegistryScope scope) {
scope.withService( ConfigurationService.class, (configurationService) -> {
assertThat( configurationService.getSettings().get( "setting" ) ).isEqualTo( "class-level" );
assertThat( configurationService.getSettings().get( "contributed" ) ).isEqualTo( "contributed-2" );
} );
}
public static class ServiceContributor1 implements ServiceContributor {
@Override
public void contribute(StandardServiceRegistryBuilder serviceRegistryBuilder) {
serviceRegistryBuilder.getSettings().put( "contributed", "contributed-1" );
}
}
public static class ServiceContributor2 implements ServiceContributor {
@Override
public void contribute(StandardServiceRegistryBuilder serviceRegistryBuilder) {
serviceRegistryBuilder.getSettings().put( "contributed", "contributed-2" );
}
}
}