Skip to content

Test instances should not be proxied in the TestContext framework [SPR-9478] #14113

Closed
@spring-projects-issues

Description

@spring-projects-issues

Peter Ertl opened SPR-9478 and commented

Background

In the TestContext framework, the DependencyInjectionTestExecutionListener (DITEL) is primarily responsible for performing dependency injection into the current test instance, but DITEL also initializes the test instance using AutowireCapableBeanFactory.initializeBean().

Javadoc for initializeBean():

Initialize the given raw bean, applying factory callbacks such as setBeanName and setBeanFactory, also applying all bean post processors (including ones which might wrap the given raw bean).

Note that no bean definition of the given name has to exist in the bean factory. The passed-in bean name will simply be used for callbacks but not checked against the registered bean definitions.

The fact that all registered bean post processors are applied to the test instance as if it were a bean in the ApplicationContext leads to unexpected side effects. For example, a CGLIB enhanced version of the test class may be created in order to proxy a transactional test class that does not implement any interfaces. The generated proxy is never actually used by the TestContext framework, and as such its creation is unnecessary and in fact unintentional.

Case Study Involving Transactional Tests and CGLIB

With a test class like the following:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration( ... )
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
@Transactional
public class TransactionalSpringTest {
  // test methods
}

... Spring proxies the test class since it discovers the @Transactional annotation. This is inappropriate since @Transactional is already handled by TransactionalTestExecutionListener via reflection. For every test method execution DependencyInjectionTestExecutionListener indirectly uses CGLIB to enhance the test class. This results in a potentially large number of instrumented CGLIB classes -- the total = number of transactional tests methods per test * number of tests -- each holding a reference to the data inside the test class which are not released but bound to the current class loader.

In our current project we have several classes (around 50-100) marked transactional which in our case results in a huge memory leak (1GB and more). We worked around this issue by letting the test case implement org.springframework.aop.Advisor, which is very dirty but blocks Spring from instrumenting the test class and works fine.

Side Note regarding Dynamic Proxies

Note, however, that if the test class implements any interface then a JDK dynamic proxy will be used instead of CGLIB enhancement. This may occur, for example, if the test class implements ApplicationContextAware like AbstractTransactionalJUnit4SpringContextTests and AbstractTransactionalTestNGSpringContextTests.

Deliverables

  1. Investigate an alternative for bean initialization of test instances that results in neither CGLIB enhancement of the test instances nor creation of dynamic proxies for test instances.

Affects: 3.0 GA

Issue Links:

3 votes, 4 watchers

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions