Skip to content

Load dedicated child ApplicationContext for test instance in the TestContext framework [SPR-4632] #9309

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
5 tasks
spring-projects-issues opened this issue Mar 26, 2008 · 15 comments
Labels
in: test Issues in the test module status: declined A suggestion or change that we don't feel we should currently apply type: enhancement A general enhancement

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Mar 26, 2008

Eberhard Wolff opened SPR-4632 and commented

Status Quo

The loadContext(\*\) methods in AbstractGenericContextLoader and AbstractGenericWebContextLoader invoke AnnotationConfigUtils.registerAnnotationConfigProcessors(context).

The result is that @Autowired, @Resource, @Inject, etc. all work by default, even if annotation-driven dependency injection is not explicitly configured in the test's ApplicationContext. Consequently, if a developer is not aware of this, then errors related to the lack of explicit annotation-driven DI support in production configuration will not be noticed until after testing which is typically undesirable.

Note, however, that this unexpected behavior only applies to XML-based configuration. With JavaConfig, an AnnotatedBeanDefinitionReader (used internally by AnnotationConfigContextLoader and AnnotationConfigWebContextLoader) automatically registers the annotation config processors.

Furthermore, BeanPostProcessors may inadvertently be applied to the test instance -- even though the test is not truly a bean in the ApplicationContext. This leads to potentially problematic behavior such as accidental proxying of the test instance, as described in #14113.


Deliverables

  1. As suggested in comments for this issue, create an ApplicationContext dedicated solely to the test instance (for the purpose of dependency injection and bean initialization) and set the parent of that context to the ApplicationContext loaded by the TCF.
  2. Stop invoking AnnotationConfigUtils.registerAnnotationConfigProcessors() in:
    • AbstractGenericContextLoader
    • AbstractGenericWebContextLoader
  3. Ensure that child contexts are properly closed with regard to context caching in the TCF.

Original Issue Summary

Do not enable annotation-driven DI for the entire ApplicationContext in the TestContext framework

Original Proposal

It would be better if the TestContext framework (TCF) only enabled annotation-driven dependency injection for test instances and not for the entire ApplicationContext.


Affects: 2.5.2

Attachments:

Issue Links:

6 votes, 6 watchers

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

This has actually been debated before but was decided to be the better tradeoff overall. We also consider enabling annotation processing by default in forthcoming special environments, so that doesn't necessarily apply to test environments only.

Actually, integration test XML files are not necessarily supposed to be an exact match for production bean definition files in the first place. There are always going to be some configuration differences, typically isolated into a specific XML file that's part of a config set. Since it's sufficient to have a context:annotation-config entry in one of the files in the config set, I don't think that the defaults in the test environment are bad there... Maybe not immediately expected but still not causing serious hassle either.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

Tuomas Kiviaho commented

Automatic post processor registration lead to intense debugging session for me since I wasn't aware of it before and I have a custom CommonAnnotationBeanPostProcessor which lead to double post processing causing latter to crash due to it's default configuration.

As workaround I just had to name my post processor identically to the default one ( (org.springframework.context.annotation.internalCommonAnnotationProcessor) preventing it from being registered.

One solution could be to use org.springframework.beans.factory.ListableBeanFactory#getBeanNamesForType to ensure that beanFactory doesn't already have post processors instead of name comparison.

@spring-projects-issues
Copy link
Collaborator Author

Tuomas Kiviaho commented

I noticed that it is possible to register processors just for test class via extendingDependencyInjectionTestExecutionListener by using test container application context as parent of a test application context which sole purpose is to instantiate test.

By moving the AnnotationConfigUtils.registerAnnotationConfigProcessors from AbstractGenericContextLoader to DependencyInjectionTestExecutionListener is one way to solve this issue without API changes. By moving hooks as well from AbstractGenericContextLoader the extendability stays about the same.

@spring-projects-issues
Copy link
Collaborator Author

Dave Syer commented

I agree with Tuomas: the test case should live in a child context, so that the behaviour of the parent context is the same as it would be in production. Can we get this into the 3.0.x roadmap?

@spring-projects-issues
Copy link
Collaborator Author

Neale Upstone commented

Is there any hope of this making 3.2 now? It looks like there are enough related changes going in with spring-test-mvc that this should go too...

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Oct 31, 2012

Sam Brannen commented

Hi Neal,

Pending completion of related issues #10284 and #14496, it may be possible to address this issue in the 3.2 time frame, but we'll just have to see how things play out.

Regards,

Sam

@spring-projects-issues
Copy link
Collaborator Author

Sam Brannen commented

FYI: I have created a branch as a sort of playground for investigating options that do not include creating a child ApplicationContext. This code may well be thrown away at some point, but in case you're interested, you can check it out here for now: https://github.com/sbrannen/spring-framework/commits/SPR-4632

Regards,

Sam

@spring-projects-issues
Copy link
Collaborator Author

Neale Upstone commented

Thanks. I've had a quick look, and do wonder if it's an approach that might lead to maintenance headaches in future. Are there drawbacks to the child context approach?

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Feb 6, 2014

Sam Brannen commented

Hi Neale,

Thanks for taking a look and more importantly for the quick feedback!

The work in the branch is really just an exploration of one possible option, but you're right: the child context approach may well prove to be more pragmatic and elegant in the long run. In addition, introducing a child context may make the issue raised in #14113 a moot point, thus potentially killing two (or more) birds with one stone. So I'll continue investigating. ;)

Cheers,

Sam

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Jul 14, 2018

Marten Deinum commented

As this would be a fix for issue #14113 is there any change of seeing this included in Spring 5.1? #14113 is an issue and with the ease of testing with @MockBean from Spring Boot not GC'ing not used contexts will become an issue (as the @MockBean is part of the key for caching the context!). 

Next to that I believe that enabling additional BeanFactoryPostProcessor and BeanPostProcessor instances on an existing context might lead to unexpected results (as Eberhard Wolff also pointed out that auto wiring magically works). 

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Jul 23, 2018

Marten Deinum commented

Sam Brannen / Juergen Hoeller any progress/information on this? As we heavily use @MockBean this is biting us quite often. We already have the "fix" from #14113 in-place but that is quite easy to circumvent (people forget to extend our base class for instance).

@spring-projects-issues
Copy link
Collaborator Author

Marten Deinum commented

Although creating a fresh ApplicationContext to use to inject dependencies into a test class might seem like a good idea, the proposed solution will also result in some issues. I did some tests with an AnnotationConfigApplicationContext!

If a test class wants access to the ApplicationContext or uses @WebAppConfiguration this would be troublesome. The first would result in injection of the temporary ApplicationContext instead of the actually used one, the use of @WebAppConfiguration would require a WebApplicationContext (which should then also leak into the DependencyInjectionTestExecutionListener). 

After some further digging, the main issue lies within the line beanFactory.initializeBean(bean, testContext.getTestClass().getName());. This results in all BeanPostProcessors being applied to the test-case. Whereas, we probably only want the init/destroy callbacks and aware callbacks to be applied. 

Sam Brannen is that along the lines of your proposed solution in your specific branch? 

@spring-projects-issues
Copy link
Collaborator Author

Sam Brannen commented

Marten Deinum, you raise some interesting questions.

I hadn't really pondered the scenario where the ApplicationContext (or WebApplicationContext (WAC)) is injected into the test instance. In theory, we could create the child context as a WAC if the parent is a WAC, but we wouldn't necessarily be able to create a child context that is the exact same type as the parent since third parties can create their own ApplicationContext implementations (e.g., Spring Boot).

I think we would have to investigate the possibilities here in greater depth with actual prototype code.

Sam Brannen is that along the lines of your proposed solution in your specific branch?

Yes, that was the goal of my proof of concept. I would actually prefer to be able to avoid the introduction of a child context while simultaneously limiting what is applied to the test instance.

The main challenge with the latter is that the core DI support in Spring is unaware of the testing framework. So there is currently no mechanism for instructing the core DI support not to do certain things (other than having the test class implement Advisor, as mentioned elsewhere as a workaround).

Juergen Hoeller, what do you think about introducing some sort of mechanism in the core DI support that would allow the TestContext framework to specify that only certain BPPs should be applied to the test instance?

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Aug 24, 2018

Sam Brannen commented

FYI: Juergen Hoeller graciously and unwittingly resolved #14113 while addressing #21674.

Thus, unnecessary proxying of test instances is no longer an issue as of Spring Framework 5.1 RC3.

@sbrannen
Copy link
Member

sbrannen commented Mar 8, 2024

Since #14113 was addressed in 5.1 and due to other enhancements in the framework in the interim, we have not heard from the community about a concrete need for loading a child context in the TCF in modern Spring applications. In addition, as stated in the above discussions, introducing a child context also introduces issues of its own.

In light of that, I am closing this issue.

@sbrannen sbrannen closed this as not planned Won't fix, can't repro, duplicate, stale Mar 8, 2024
@sbrannen sbrannen removed this from the General Backlog milestone Mar 8, 2024
@sbrannen sbrannen removed their assignment Mar 8, 2024
@sbrannen sbrannen added the status: declined A suggestion or change that we don't feel we should currently apply label Mar 8, 2024
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 status: declined A suggestion or change that we don't feel we should currently apply type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

2 participants