Skip to content

Support parameter injection in @BeforeTransaction/@AfterTransaction methods #30736

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
manueljordan opened this issue Jun 24, 2023 · 6 comments
Closed
Assignees
Labels
in: test Issues in the test module type: enhancement A general enhancement
Milestone

Comments

@manueljordan
Copy link

manueljordan commented Jun 24, 2023

For Spring Framework 6.0.4 --- regarding testing with JUnit Jupiter -- the following works fine.

@AfterEach
void afterEach(@Autowired CienciaService cienciaService) {
      System.out.println("afterEach ...");
      ...
}

The point is that an @AfterEach method accepts injection through parameters at runtime.
Same as @BeforeAll, I am assuming @BeforeEach works as well.

Now the following:

@AfterTransaction
void afterTransaction(@Autowired CienciaService cienciaService) {
      System.out.println("afterTransaction ...");
      ...
}

That compiles, but at runtime we get:

Jun 23, 2023 7:04:05 PM org.springframework.test.context.transaction.TransactionalTestExecutionListener runAfterTransactionMethods
SEVERE: Exception encountered while executing @AfterTransaction method [void com.manuel.jordan.service.process.ProcessServiceTests.afterTransaction(com.manuel.jordan.service.CienciaService,com.manuel.jordan.service.CientificoService)] for test context [DefaultTestContext@30cdae70 testClass = com.manuel.jordan.service.process.ProcessServiceTests, testInstance = com.manuel.jordan.service.process.ProcessServiceTests@1654a892, testMethod = processServiceCreateV2Test(CienciaService, CientificoService), testException = null, mergedContextConfiguration = [MergedContextConfiguration@3163987e testClass = com.manuel.jordan.service.process.ProcessServiceTests, locations = [], classes = [com.manuel.jordan.config.AppConfig], contextInitializerClasses = [], activeProfiles = ["pre-prod", "cache", "exception-unchecked-try-catch-non"], propertySourceLocations = [], propertySourceProperties = [], contextCustomizers = [], contextLoader = org.springframework.test.context.support.DelegatingSmartContextLoader, parent = null], attributes = {"org.springframework.test.context.event.ApplicationEventsTestExecutionListener.recordApplicationEvents" -> false}]
java.lang.IllegalArgumentException: wrong number of arguments
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)

Questions

What is the reason for this?

I am assuming that @BeforeTransaction has the same situation. Currently as a workaround I use:

@Autowired
ApplicationContext applicationContext;
...

@AfterTransaction
void afterTransaction() {
      CienciaService cienciaService = applicationContext.getBean(CienciaService.class);
      System.out.println("afterTransaction ...");
      ...  
}

Note the injected @Service classes are @Transactional

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Jun 24, 2023
@sbrannen sbrannen changed the title Let a method with @AfterTransaction accept Injection through Parameter(s) Support parameter injection in @BeforeTransaction/`@AfterTransaction methods Jun 25, 2023
@sbrannen sbrannen added in: test Issues in the test module type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Jun 25, 2023
@sbrannen sbrannen self-assigned this Jun 25, 2023
@sbrannen sbrannen added this to the 6.1.x milestone Jun 25, 2023
@sbrannen sbrannen changed the title Support parameter injection in @BeforeTransaction/`@AfterTransaction methods Support parameter injection in @BeforeTransaction/@AfterTransaction methods Jun 25, 2023
@sbrannen sbrannen modified the milestones: 6.1.x, 6.1.0-RC1 Aug 19, 2023
@sbrannen
Copy link
Member

sbrannen commented Sep 9, 2023

What is the reason for this?

The reason is that JUnit Jupiter invokes JUnit lifecycle methods such as @BeforeAll, @AfterEach, etc.

Whereas, Spring invokes @BeforeTransaction and @AfterTransaction methods, and Spring has done that agnostic of the underlying testing framework since long before JUnit Jupiter existed.

Fortunately, it should be possible for Spring to delegate to JUnit Jupiter to invoke @BeforeTransaction and @AfterTransaction methods with parameter injection by using the ExecutableInvoker API that was introduced in JUnit Jupiter 5.9.

Of course, that means this functionality would only be available to JUnit Jupiter tests (and not to JUnit 4 or TestNG tests). So we will have to introduce a Spring-specific abstraction over the ExecutableInvoker API for Spring TestExecutionListener implementations.

I'll see what's possible...

@manueljordan
Copy link
Author

Thanks for the feedback, if is not possible, the current reference documentation should be updated to indicate this "constraint".

sbrannen added a commit to sbrannen/spring-framework that referenced this issue Sep 10, 2023
@sbrannen
Copy link
Member

FWIW, I actually documented this in JUnit 5's issue tracker quite a long time ago. 😉

junit-team/junit5#2191 (comment)

@sbrannen
Copy link
Member

@manueljordan
Copy link
Author

FWIW, I actually documented this in JUnit 5's issue tracker quite a long time ago. 😉
junit-team/junit5#2191 (comment)

Thanks for the link, but I meant about Spring Framework Reference documentation. Yes, it is tricky: in what documentation should be located this case. For JUnit or SF?, because Autowire and Transactions is involved I expected to see this in the SF Reference documentation. I hope you see my point

sbrannen added a commit that referenced this issue Sep 10, 2023
In order to be able to support parameter injection in
@​BeforeTransaction and @​AfterTransaction methods (see gh-30736), this
commit introduces a MethodInvoker API for TestExecutionListeners as a
generic mechanism for delegating to the underlying testing framework to
invoke methods.

The default implementation simply invokes the method without arguments,
which allows TestExecutionListeners using this mechanism to operate
correctly when the underlying testing framework is JUnit 4, TestNG, etc.

A JUnit Jupiter specific implementation is registered in the
SpringExtension which delegates to the
ExtensionContext.getExecutableInvoker() mechanism introduced in JUnit
Jupiter 5.9. This allows a TestExecutionListener to transparently
benefit from registered ParameterResolvers in JUnit Jupiter (including
the SpringExtension) when invoking user methods, effectively providing
support for parameter injection for arbitrary methods.

Closes gh-31199
@sbrannen
Copy link
Member

sbrannen commented Sep 11, 2023

This has been addressed in ed83461.

Feel free to try it out in 6.1 M5 snapshot builds.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: test Issues in the test module type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

3 participants