diff --git a/hibernate-testing/README.adoc b/hibernate-testing/README.adoc index 4d8f4000cc..f31ca76d5b 100644 --- a/hibernate-testing/README.adoc +++ b/hibernate-testing/README.adoc @@ -1,4 +1,7 @@ The `hibernate-testing` module of Hibernate ORM defines utilities for writing -tests easier.... +tests easier. -// todo : write more (duh) \ No newline at end of file +This documentation focuses on the JUnit 5 & annotation based testing support... + + +// todo (6.1) : write more (duh) \ No newline at end of file diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DomainModelExtension.java b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DomainModelExtension.java index fa56aa5adc..fb7ebcfc98 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DomainModelExtension.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DomainModelExtension.java @@ -6,6 +6,7 @@ */ package org.hibernate.testing.orm.junit; +import java.lang.reflect.Method; import java.util.Iterator; import java.util.Locale; import java.util.Optional; @@ -48,12 +49,62 @@ public class DomainModelExtension private static final String MODEL_KEY = MetadataImplementor.class.getName(); + /** + * Intended for use from external consumers. Will never create a scope, just + * attempt to consume an already created and stored one + */ + public static DomainModelScope findDomainModelScope(Object testInstance, ExtensionContext context) { + final ExtensionContext.Store store = locateExtensionStore( testInstance, context ); + final DomainModelScope existing = (DomainModelScope) store.get( MODEL_KEY ); + if ( existing != null ) { + return existing; + } + + throw new RuntimeException( "Could not locate DomainModelScope : " + context.getDisplayName() ); + } + + public static DomainModelScope resolveForMethodLevelSessionFactoryScope(ExtensionContext context) { + assert context.getTestMethod().isPresent(); + + // if the test defines `@DomainModel` at the class-level but not at the method-level, + // we will run into problems with a shared `TypeConfiguration`. In this case, we need + // to create a new DomainModelScope (well ask the DomainModelExtension to create it) + // and store it relative to the method-context + // + // first, look for `@DomainModel` at the method-level. if it is there, no problem. + // otherwise we need to "act like it is" and create a method-level DomainModelScope + + final Object testInstance = context.getRequiredTestInstance(); + final Method testMethod = context.getRequiredTestMethod(); + + final Optional methodLevelAnnRef = AnnotationSupport.findAnnotation( + testMethod, + DomainModel.class + ); + if ( methodLevelAnnRef.isPresent() ) { + // just return the existing one + return findDomainModelScope( testInstance, context ); + } + + final Optional classLevelAnnRef = AnnotationSupport.findAnnotation( + context.getRequiredTestClass(), + DomainModel.class + ); + + if ( classLevelAnnRef.isPresent() + || testInstance instanceof DomainModelProducer ) { + final DomainModelScope created = createDomainModelScope( testInstance, classLevelAnnRef, context ); + locateExtensionStore( testInstance, context ).put( MODEL_KEY, created ); + return created; + } + + throw new RuntimeException( "Could not locate @DomainModel to use with method-level @SessionFactory: " + context.getDisplayName() ); + } + @Override public void postProcessTestInstance(Object testInstance, ExtensionContext context) { - assert context.getTestClass().isPresent(); - final Optional domainModelAnnRef = AnnotationSupport.findAnnotation( - context.getElement().get(), + context.getRequiredTestClass(), DomainModel.class ); @@ -66,10 +117,6 @@ public class DomainModelExtension @Override public void beforeEach(ExtensionContext context) { - assert context.getTestMethod().isPresent(); - assert context.getRequiredTestMethod() == context.getElement().get(); - assert context.getTestInstance().isPresent(); - final Optional domainModelAnnRef = AnnotationSupport.findAnnotation( context.getElement().get(), DomainModel.class @@ -198,16 +245,6 @@ public class DomainModelExtension return scope; } - public static DomainModelScope findDomainModelScope(Object testInstance, ExtensionContext context) { - final ExtensionContext.Store store = locateExtensionStore( testInstance, context ); - final DomainModelScope existing = (DomainModelScope) store.get( MODEL_KEY ); - if ( existing != null ) { - return existing; - } - - throw new RuntimeException( "Could not locate @DomainModel annotation : " + context.getDisplayName() ); - } - protected static void applyCacheSettings(Metadata metadata, boolean overrideCacheStrategy, String cacheConcurrencyStrategy) { if ( !overrideCacheStrategy ) { return; diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/ServiceRegistryExtension.java b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/ServiceRegistryExtension.java index 6ce6b6070e..461efe6c44 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/ServiceRegistryExtension.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/ServiceRegistryExtension.java @@ -61,10 +61,7 @@ public class ServiceRegistryExtension } @Override - public void beforeEach(ExtensionContext context) throws Exception { - assert context.getTestMethod().isPresent(); - assert context.getRequiredTestMethod() == context.getElement().get(); - + public void beforeEach(ExtensionContext context) { Optional bsrAnnRef = AnnotationSupport.findAnnotation( context.getElement().get(), BootstrapServiceRegistry.class diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/SessionFactoryExtension.java b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/SessionFactoryExtension.java index 6739b84901..c777fd5ebd 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/SessionFactoryExtension.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/SessionFactoryExtension.java @@ -30,7 +30,7 @@ import org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.ActionGroup import org.hibernate.testing.jdbc.SQLStatementInspector; import org.hibernate.testing.orm.transaction.TransactionUtil; -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.TestExecutionExceptionHandler; import org.junit.jupiter.api.extension.TestInstancePostProcessor; @@ -48,15 +48,15 @@ import org.jboss.logging.Logger; * @author Steve Ebersole */ public class SessionFactoryExtension - implements TestInstancePostProcessor, AfterAllCallback, TestExecutionExceptionHandler { + implements TestInstancePostProcessor, BeforeEachCallback, TestExecutionExceptionHandler { private static final Logger log = Logger.getLogger( SessionFactoryExtension.class ); private static final String SESSION_FACTORY_KEY = SessionFactoryScope.class.getName(); - private static ExtensionContext.Store locateExtensionStore(Object testInstance, ExtensionContext context) { - return JUnitHelper.locateExtensionStore( SessionFactoryExtension.class, context, testInstance ); - } - + /** + * Intended for use from external consumers. Will never create a scope, just + * attempt to consume an already created and stored one + */ public static SessionFactoryScope findSessionFactoryScope(Object testInstance, ExtensionContext context) { final ExtensionContext.Store store = locateExtensionStore( testInstance, context ); final SessionFactoryScope existing = (SessionFactoryScope) store.get( SESSION_FACTORY_KEY ); @@ -64,9 +64,55 @@ public class SessionFactoryExtension return existing; } - SessionFactoryProducer producer = null; + throw new RuntimeException( "Could not locate SessionFactoryScope : " + context.getDisplayName() ); + } - final DomainModelScope domainModelScope = DomainModelExtension.findDomainModelScope( testInstance, context ); + @Override + public void postProcessTestInstance(Object testInstance, ExtensionContext context) { + log.tracef( "#postProcessTestInstance(%s, %s)", testInstance, context.getDisplayName() ); + + final Optional sfAnnRef = AnnotationSupport.findAnnotation( + context.getRequiredTestClass(), + SessionFactory.class + ); + + if ( sfAnnRef.isPresent() + || SessionFactoryProducer.class.isAssignableFrom( context.getRequiredTestClass() ) ) { + final DomainModelScope domainModelScope = DomainModelExtension.findDomainModelScope( testInstance, context ); + final SessionFactoryScope created = createSessionFactoryScope( testInstance, sfAnnRef, domainModelScope, context ); + locateExtensionStore( testInstance, context ).put( SESSION_FACTORY_KEY, created ); + } + } + + @Override + public void beforeEach(ExtensionContext context) { + final Optional sfAnnRef = AnnotationSupport.findAnnotation( + context.getRequiredTestMethod(), + SessionFactory.class + ); + + if ( sfAnnRef.isEmpty() ) { + // assume the annotations are defined on the class-level... + // will be validated by the parameter-resolver or SFS-extension + return; + } + + final DomainModelScope domainModelScope = DomainModelExtension.resolveForMethodLevelSessionFactoryScope( context ); + final SessionFactoryScope created = createSessionFactoryScope( context.getRequiredTestInstance(), sfAnnRef, domainModelScope, context ); + final ExtensionContext.Store extensionStore = locateExtensionStore( context.getRequiredTestInstance(), context ); + extensionStore.put( SESSION_FACTORY_KEY, created ); + } + + private static ExtensionContext.Store locateExtensionStore(Object testInstance, ExtensionContext context) { + return JUnitHelper.locateExtensionStore( SessionFactoryExtension.class, context, testInstance ); + } + + private static SessionFactoryScopeImpl createSessionFactoryScope( + Object testInstance, + Optional sfAnnRef, + DomainModelScope domainModelScope, + ExtensionContext context) { + SessionFactoryProducer producer = null; if ( testInstance instanceof SessionFactoryProducer ) { producer = (SessionFactoryProducer) testInstance; @@ -76,13 +122,8 @@ public class SessionFactoryExtension throw new RuntimeException( "Unable to determine how to handle given ExtensionContext : " + context.getDisplayName() ); } - final Optional sfAnnWrappper = AnnotationSupport.findAnnotation( - context.getElement().get(), - SessionFactory.class - ); - - if ( sfAnnWrappper.isPresent() ) { - final SessionFactory sessionFactoryConfig = sfAnnWrappper.get(); + if ( sfAnnRef.isPresent() ) { + final SessionFactory sessionFactoryConfig = sfAnnRef.get(); producer = model -> { try { @@ -131,8 +172,6 @@ public class SessionFactoryExtension producer ); - locateExtensionStore( testInstance, context ).put( SESSION_FACTORY_KEY, sfScope ); - if ( testInstance instanceof SessionFactoryScopeAware ) { ( (SessionFactoryScopeAware) testInstance ).injectSessionFactoryScope( sfScope ); } @@ -180,32 +219,6 @@ public class SessionFactoryExtension ); } - @Override - public void postProcessTestInstance(Object testInstance, ExtensionContext context) { - log.tracef( "#postProcessTestInstance(%s, %s)", testInstance, context.getDisplayName() ); - - findSessionFactoryScope( testInstance, context ); - } - - @Override - public void afterAll(ExtensionContext context) { - log.tracef( "#afterAll(%s)", context.getDisplayName() ); - - final Object testInstance = context.getRequiredTestInstance(); - - if ( testInstance instanceof SessionFactoryScopeAware ) { - ( (SessionFactoryScopeAware) testInstance ).injectSessionFactoryScope( null ); - } - - final SessionFactoryScopeImpl removed = (SessionFactoryScopeImpl) locateExtensionStore( testInstance, context ).remove( SESSION_FACTORY_KEY ); - if ( removed != null ) { - removed.close(); - } - - if ( testInstance instanceof SessionFactoryScopeAware ) { - ( (SessionFactoryScopeAware) testInstance ).injectSessionFactoryScope( null ); - } - } @Override public void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable { diff --git a/hibernate-testing/src/test/java/org/hibernate/testing/annotations/methods/SessionFactoryTesting.java b/hibernate-testing/src/test/java/org/hibernate/testing/annotations/methods/SessionFactoryTesting.java new file mode 100644 index 0000000000..ad3e7f0aa4 --- /dev/null +++ b/hibernate-testing/src/test/java/org/hibernate/testing/annotations/methods/SessionFactoryTesting.java @@ -0,0 +1,37 @@ +/* + * 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.stat.spi.StatisticsImplementor; + +import org.hibernate.testing.annotations.AnEntity; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Steve Ebersole + */ +@DomainModel( annotatedClasses = AnEntity.class ) +@SessionFactory( generateStatistics = true ) +public class SessionFactoryTesting { + @Test + public void testClassLevelAnnotations(SessionFactoryScope scope) { + final StatisticsImplementor statistics = scope.getSessionFactory().getStatistics(); + assertThat( statistics.isStatisticsEnabled() ).isTrue(); + } + + @Test + @SessionFactory() + public void testMethodLevelAnnotations(SessionFactoryScope scope) { + final StatisticsImplementor statistics = scope.getSessionFactory().getStatistics(); + assertThat( statistics.isStatisticsEnabled() ).isFalse(); + } +}