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

Dependency injection of @Configurable objects should work across test suites [SPR-6121] #10789

Open
spring-issuemaster opened this issue Sep 16, 2009 · 17 comments

Comments

@spring-issuemaster
Copy link
Collaborator

commented Sep 16, 2009

Marc Ludwig opened SPR-6121 and commented

Overview

We have a large number of unit/integration tests that assert behavior within our system and rely upon injection of dependencies into configurable domain objects.

These tests all work when executed individually; however, when executed within a suite (either through the IDE or Ant) certain tests fail as dependencies have not been injected into the @Configurable objects. We have also seen dependencies that were configured for test X being injected into test Y rather than the dependencies for test Y; but I have no test case for this.


Steps to Reproduce

I have created a set of three tests -- tests 1 and 3 are basically identical. If these are executed in a suite the third test will fail as the dependency is not injected into the @Configurable object even though it is available to the test. This only occurs if test 2 is a Spring test.

See attached zip file.


Further Resources


Affects: 2.5.6, 3.0.5

Reference URL: http://forum.springsource.org/showthread.php?t=77980

Attachments:

Issue Links:

  • #15989 SpringContexts not properly closed after test-class is finished ("is duplicated by")
  • #11019 TestContext framework should support one AspectJ instance per ApplicationContext
  • #17123 AnnotationTransactionAspect retains reference to JpaTransactionManager from closed context

11 votes, 14 watchers

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 16, 2009

Marc Ludwig commented

Further info.
Please note that I have converted the project to use Compile Time Weaving (Eclipse AJDT) and the problem still occurrs.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 28, 2009

Marc Ludwig commented

Hi
Can you advise whether I should expect any progress on this issue.

Can any workaround be suggested, or is the usage of the Spring Test components as per the sample incorrect?

It is currently causing major issues for my employer, and currently I can not find any suitable workaround or fix.

Regards
Marc Ludwig

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Oct 1, 2009

Sam Brannen commented

Hi Marc,

I've not yet had time to look into this. That's why this issue has been assigned to 3.1 RC1.

However, you could try annotating your test methods with @DirtiesContext to see if that has any positive effect.

Regards,

Sam

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Oct 21, 2009

Marc Ludwig commented

Within the test case, use of @DirtiesContext on the test methods does solve the issue. However within my production environment this would mean annotating most (if not all) Spring injected tests as order of tests impacts the failures.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Nov 12, 2009

Tom Denley commented

Hi,

Is there any ongoing activity on this? I'd appreciate an update if possible.

Many thanks,
Tom

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Mar 25, 2010

Marc Ludwig commented

Hi
I see that this is currently assigned to 3.1 M2.
Does it look like this issue will be looked at within the timeline of this release?

Thanks
Marc

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Apr 10, 2010

Sam Brannen commented

Related forum thread: http://forum.springsource.org/showthread.php?t=48088

Excerpt:

using Spring 2.5 UnitTests all combinations of context locations are stored isolated in a context cache. @DirtiesContext allows to invalidate the own cached combination of context-locations.
However this isolation is destroyed if one injects into aspectJ-woven Aspects.

AspectJ works with its own Singleton. This Singleton is injected everytime a context locations combination is newly created and put into Spring's context cache.

Unfortunately this means if I run 3 subsequent unit-tests:

(1) ac-a.xml injecting foo into my aspect
(2) ac-b.xml injecting bar into my aspect
(3) ac-a.xml injecting foo into my aspect

then (3) uses caching od ac-a and therfore bar is still injected into my Aspect. So my Unit-Tests' isolation is destroyed as the execution of (2) affects the outcome of (3).

Unfortunately at the end of exectuting (2) it is not possible to dirty / flush the context cached for ac-a (works only for the own context cache entry). We tried to use Testlisteners to flush the whole context-cache but there is no access to the cache. Is there any way to flush the whole Cache??

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Apr 10, 2010

Sam Brannen commented

Custom solution posted in the following blog:

http://object-to-string.blogspot.com/2010/04/make-configurable-work-with-spring-256.html

public class MySpringJUnit4ClassRunner extends SpringJUnit4ClassRunner {

 public MySpringJUnit4ClassRunner(Class clazz) throws InitializationError {
  super(clazz);
  getTestContextManager().registerTestExecutionListeners(
    new AbstractTestExecutionListener() {

   @Override
   public void prepareTestInstance(TestContext testContext)
     throws Exception {
   
    Aspects.aspectOf(AnnotationBeanConfigurerAspect.class)
     .setBeanFactory(((GenericApplicationContext)testContext.getApplicationContext())
      .getDefaultListableBeanFactory());
    
   }
  });
 }
 
}

Note, however, that the above anonymous AbstractTestExecutionListener class should preferably be a stand-alone TestExecutionListener registered via @TestExecutionListeners. In that manner, there is no need to subclass SpringJUnit4ClassRunner.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Apr 11, 2010

Matthew Wise commented

On behalf of Marc Ludwig:

I can confirm that the workaround posted above works for our simple test case. We will now try registering a TestExecutionListener instead.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Apr 12, 2010

Matthew Wise commented

Registering a TestExecutionListener also works (However we already have a custom subclassed SpringJUnit4ClassRunner so will go with option 1).

I wouldn't consider this a fix, it is a workaround, however it will solve our problems.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Jun 28, 2010

Frederik De Milde commented

Hello,
due to some hibernate issues we also added

Aspects.aspectOf(AnnotationBeanConfigurerAspect.class).destroy();

so the TestExecutionListener should become like

new AbstractTestExecutionListener() {
   @Override
   public void prepareTestInstance(TestContext testContext)
     throws Exception {
    Aspects.aspectOf(AnnotationBeanConfigurerAspect.class).destroy();
    Aspects.aspectOf(AnnotationBeanConfigurerAspect.class)
     .setBeanFactory(((GenericApplicationContext)testContext.getApplicationContext()).getDefaultListableBeanFactory());
    
   }
  }
@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Mar 20, 2011

Michael Hunger commented

Perhaps the easiest way to solve that is to make a clearCache() method on the default TestContext available. (That also shuts down each of the removed contexts. And even better provide a means to call that, e.g. a separate TestExecutionListener or a flag on @DirtiesContext(clearCache=true)

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Mar 20, 2011

Michael Hunger commented

Destroying the aspects only helps when you know all the aspects that are involved. For instance the @Transactional aspect also holds onto the old context, so whenever you for instance use that, an different transaction-manager could be used than the one you have configured in your current context.

The root cause of this issue is that the context is cached and not reloaded when the third test runs. This should be also noted in different places in the documentation.

Perhaps one should rather add those things as flags to @ContextConfiguration and not a separate @DirtiesContext, because the concern belongs there.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Feb 18, 2013

DK commented

I'm using Spring 3.1.1-RELEASE with spring-aspects and aspectjrt 1.7.1

I tried both of the above workarounds to no avail. I tried both:

@RunWith(MySpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath*:/MyFirtTest-context.xml")
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class MyFirtTest {
    ...
}
@RunWith(MySpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath*:/MySecondTest-context.xml")
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class MySecondTest {
    ...
}

If I add @Ignore to one of the tests everything works.
However, if I include both tests then dependencies don't get injected with @Configurable.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented May 30, 2013

Neale Upstone commented

I've just hit this one. It's a horrid pain debugging to know enough to come and find this bug report (and I suspect some people never get here).

I agree with Michael Hunger that it's a ContextConfiguration concern. I think we should have an additional default TestExecutionListener, as shown in this snippet from what I've ended up with.

@TestExecutionListeners({ServletTestExecutionListener.class, // Defaults plus new one
    DependencyInjectionTestExecutionListener.class,
    DirtiesContextTestExecutionListener.class,
    TransactionalTestExecutionListener.class,
    AbstractIntegrationTest.AspectBeanFactoryResettingListener.class})
@Transactional
public abstract class AbstractIntegrationTest {

    public static final class AspectBeanFactoryResettingListener extends AbstractTestExecutionListener {
        @Override
        public void prepareTestInstance(TestContext testContext) throws Exception {

            DefaultListableBeanFactory factory = ((GenericApplicationContext) testContext.getApplicationContext())
                    .getDefaultListableBeanFactory();
            AnnotationBeanConfigurerAspect.aspectOf().setBeanFactory(factory);
            AnnotationTransactionAspect.aspectOf().setBeanFactory(factory);
        }
    }

...
}

Naturally, there'd be a bit of work to deal with the scenarios when spring-aspects is not present, but I think a new default listener would save lots of people lots of pain (and I'd have been having my lunch an hour ago intead of rushing off now ;-) )

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented May 30, 2013

Neale Upstone commented

BTW. There's a related bug, #9829, which may merit some consideration here.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Nov 9, 2015

Eric B commented

I've just encountered this problem / bug myself. I'm amazed that after 6 years, it is still open! Is this so difficult to add to the SpringJUnit4ClassRunner class? Or are there a lot of other considerations that have to be considered?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
1 participant
You can’t perform that action at this time.