Skip to content
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

Introduce EnableTestScopedConstructorContext annotation for extensions #4032

Merged
merged 4 commits into from
Oct 8, 2024

Conversation

JojOatXGME
Copy link
Contributor

@JojOatXGME JojOatXGME commented Sep 29, 2024

Overview

Resolve #1568.
Resolve #2970.
Resolve #3445.

This PR moves the instance creation into the execution context of the test method. This affects the following callbacks:

  • TestInstancePreConstructCallback
  • TestInstanceFactory
  • ParameterResolver (when resolving parameters for the constructor)
  • TestInstancePostProcessor

Assuming the test is using TestInstance.Lifecycle.PER_METHOD (the default), these callbacks will now receive the ExtensionContext for the currently executed test method. This has the following effects:

  • The callbacks can now access ExtensionContext.getTestMethod()
  • ExtensionContext.getTestClass() and TestInstanceFactoryContext.getTestClass() are no-longer interchangeable
  • ExtensionContext.getTestClass() now returns the (nested) class of the currently executed test
  • Instances of CloseableResource which are added to the store will now be closed at the end of the test execution

If the test is using TestInstance.Lifecycle.PER_CLASS, the behavior is unaltered.

Not sure if the change is considered API breaking. The previous behavior was rather unintuitive and not explicitly documented. However, the example at TestInfoDemo is no-longer valid and has been changed as part of the PR. I also think there is a good chance that there are a few extensions in the wild which rely on the previous behavior, since the behavior was in place for multiple years. I would therefore like to know how I should proceed with the PR.


I hereby agree to the terms of the JUnit Contributor License Agreement.


Definition of Done

@marcphilipp
Copy link
Member

see #3445 (comment)

Copy link
Contributor Author

@JojOatXGME JojOatXGME left a comment

Choose a reason for hiding this comment

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

I introduced the annotation @EnableTestScopedConstructorContext to enable the new behavior for TestInstancePreConstructCallback, TestInstancePostProcessor and TestInstanceFactory. I have few remaining questions, which I have added as review comments.

@Retention(RetentionPolicy.RUNTIME)
@Inherited
@API(status = MAINTAINED, since = "5.12")
public @interface EnableTestScopedConstructorContext {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Feel free to suggest other names. This is the best name that came to mind. I think the name is good enough, at least I like it much more than my other ideas (e.g. NewTestInstanceConstructionContext).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I just realized that you have already suggested @MethodLevelExtensionContextAware. I don't have a strong preference. Your suggestion is a bit shorter.

Copy link
Member

Choose a reason for hiding this comment

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

Let's stick with your name for now. I'll brainstorm with the team in our next team call.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I just wanted to note, that after the idea of a common method has lingered in my head over the last day, I have started to prefer it. The idea would be to add a default method to the empty Extension interface. At the end, I don't think any of my reasons is that significant, and I am totally happy with the current solution as well. Anyway, here are the reasons why I started to like the idea of a default method, although it is more clunky to use.

  • Improved discoverability as the method is shown by the IDE when you look for methods to override (e.g. Ctrl+I in IntelliJ). However, the Javadoc also makes it rather easy to find already.
  • Extensions can use arbitrary logic for the flag. For example, a wrapper or proxy which delegates all method calls to another extension would be able to preserve the flag from the delegate. However, it is probably a very special use case and not that relevant.
  • Less likely to impact the performance without having to introduce any cache.
  • Maybe more consistent, as I think there are currently no other annotations used in the extension logic.
  • A default method could technically be removed again without breaking binary compatibility (ABI). However, I assume removing it from the API again is not really an option anyway, so it is not really relevant.

Feel free to pick whatever you prefer. I just wanted to communicate my thoughts.

Copy link
Member

Choose a reason for hiding this comment

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

Those are good points! To get a better feel for it, I've given it a try in #4062. I may be subject to the IKEA effect now, but it like the solution better for the reasons you've given above.

@Retention(RetentionPolicy.RUNTIME)
@Inherited
@API(status = MAINTAINED, since = "5.12")
public @interface EnableTestScopedConstructorContext {
Copy link
Member

Choose a reason for hiding this comment

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

Let's stick with your name for now. I'll brainstorm with the team in our next team call.

@marcphilipp marcphilipp self-requested a review October 8, 2024 08:52
@marcphilipp marcphilipp changed the title Fix ExtensionContext on instance creation Introduce EnableTestScopedConstructorContext annotation for extensions Oct 8, 2024
@marcphilipp
Copy link
Member

I'm going ahead with merging this PR. I will address the remaining work in #4057.

@marcphilipp marcphilipp merged commit c2fb8bd into junit-team:main Oct 8, 2024
15 checks passed
@marcphilipp
Copy link
Member

Thanks a lot, @JojOatXGME! 👍

@JojOatXGME
Copy link
Contributor Author

Also thanks from my site. I am happy to remove my workaround in my projects once it is released. 😄 Feel free to reach out if you have any questions, otherwise I will just leave it to you now.

@JojOatXGME JojOatXGME deleted the issues/3445 branch October 8, 2024 23:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants