Skip to content

Conversation

RAJ-debug858
Copy link

I've successfully implemented a solution to address the Hibernate session and transaction issue in Grails
integration tests. The solution provides a way for integration tests to run with the same transactional context
as the application.

What was implemented:

  1. @WithSession annotation
    (grails-testing-support-core/src/main/groovy/grails/testing/mixin/integration/WithSession.groovy):
    - New annotation that binds a Hibernate session without starting a transaction
    - Can be applied at class or method level
    - Supports specifying specific datasources
  2. Session binding infrastructure:
    - GrailsTestSessionInterceptor - Manages session binding without transactions
    - WithSessionSpecExtension - Spock extension for handling @WithSession
    - WithSessionTransformation - AST transformation for the annotation
  3. Enhanced test infrastructure:
    - Updated GrailsTestMode to support bindSession flag
    - Modified GrailsTestInterceptor to handle session-only binding
    - Updated IntegrationSpecConfigurerExtension to enable session binding by default
  4. Test examples:
    - WithSessionIntegrationSpec - Demonstrates usage of @WithSession
    - SessionBindingComparisonSpec - Shows differences between test modes
  5. Documentation:
    - Added comprehensive documentation in the integration testing guide

How it solves the problem:

The issue described three scenarios:

  • Without @Rollback: No session bound → tests fail for operations that work in the app
  • With @Rollback: Both session and transaction → tests pass for operations that fail in the app
  • With @WithSession: Session without transaction → tests match application behavior

The new @WithSession annotation provides a session binding that matches the OISV (Open Session In View) pattern
used in running applications, where:

  • SELECT operations work (session available)
  • save() without flush works (no transaction needed)
  • save(flush: true) fails without @transactional (matches runtime behavior)

This ensures integration tests accurately reflect the application's runtime behavior, preventing false positives
and false negatives in test results.

@jamesfredley
Copy link
Contributor

@RAJ-debug858 Thank you. Can you resubmit with the a target branch of 7.0.x?

When I try to make this change I get "There are no new commits between base branch '7.0.x' and head branch 'fix/hibernate-session-integration-tests-14920'", so I am guessing some of your new commits are not present on your forked branch.

@jdaugherty
Copy link
Contributor

FYI: you can add your branch as a remote, fetch, cherry-pick your change, and then adjust for where its supposed to be. GitHub is showing 10,000+ commits so my guess is this change was made against an earlier branch before we created the mono repo.

@RAJ-debug858 RAJ-debug858 changed the base branch from master to 7.0.x August 22, 2025 03:15
@RAJ-debug858
Copy link
Author

RAJ-debug858 commented Aug 22, 2025

@jamesfredley Thank you! I’ve rebased my changes onto the 7.0.x branch and force-pushed the updated branch. The PR now targets 7.0.x as requested.

@RAJ-debug858
Copy link
Author

  • grails-test-core/build.gradle (modified)
    • grails-testing-support-core/build.gradle (modified)
    • The moved files in grails-testing-support-core/src/main/groovy/org/grails/test/
    • grails-testing-support-core/src/main/resources/META-INF/services/org.spockframework.runtime.extension.IGlobalE
      xtension (modified)
    • The fixed Java file with the import change

@@ -47,7 +47,8 @@ dependencies {

api project(':grails-databinding')
api project(':grails-datamapping-core')
api project(':grails-test-core')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test-core is meant to be the parent to this dependency. Why are you removing it?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Restored grails-test-core dependency in grails-testing-support-core

@@ -31,6 +31,7 @@ dependencies {
implementation platform(project(':grails-bom'))

api 'org.springframework:spring-tx'

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The extra newline should be removed

Copy link
Contributor

@jdaugherty jdaugherty left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hibernate considers OSIV an anti pattern. I understand that previous Grails applications have used it and I fully support adding test support for the feature, but we should not make this behavior the default given that it's considered bad practice upstream.

@@ -48,6 +48,7 @@ dependencies {
api project(':grails-databinding')
api project(':grails-datamapping-core')
api project(':grails-test-core')
api('org.spockframework:spock-core') { transitive = false }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is incudes by test-core so it should be unnecessary

@@ -29,7 +29,7 @@
* @author Graeme Rocher
* @since 3.0
*/
@ContextConfiguration(loader = GrailsApplicationContextLoader.class)
@ContextConfiguration(loader = SpringBootContextLoader.class)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why aren't we using the Grails Context?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay we should use the Grails-specific context loader since it provides Grails
application context setup rather than just generic Spring Boot context. The GrailsApplicationContextLoader
extends SpringBootContextLoader but adds Grails-specific initialization through GrailsApp.

@jdaugherty
Copy link
Contributor

Can't the test behavior be driven by whether the application uses OSIV?

@@ -60,7 +60,10 @@ class IntegrationSpecConfigurerExtension implements IAnnotationDrivenExtension<A

IntegrationSpecMethodInterceptor(ApplicationContext applicationContext) {
this.applicationContext = applicationContext
this.mode = new GrailsTestMode(autowire: true, wrapInTransaction: true, wrapInRequestEnvironment: true)
// By default, bind session for integration tests to match application behavior
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The application behavior depends if OSIV is configured and if hibernate is used. We should have our tests default to best behaviors, or even better, detect the configuration and then only apply it based on the application configuration.

@@ -1 +1 @@
org.grails.testing.spock.TestingSupportExtension
org.grails.test.spock.WithSessionSpecExtension
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about the original extension? Why are we replacing the old behavior instead of augmenting it?

@CompileStatic
class GrailsTestSessionInterceptor {

private static final Logger LOG = LoggerFactory.getLogger(GrailsTestSessionInterceptor)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a groovy class, is there a reason you can't use @Slf4j ?

@@ -30,6 +30,7 @@ class GrailsTestInterceptor {
private String[] testClassSuffixes

private GrailsTestTransactionInterceptor transactionInterceptor
private GrailsTestSessionInterceptor sessionInterceptor
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not compiling.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: In Progress
Development

Successfully merging this pull request may close these issues.

3 participants