18

I have a particular class (let's say MyTest) in my Spring integration tests that is using PowerMock @PrepareForTest annotation on a Spring component: @PrepareForTest(MyComponent.class). This means that PowerMock will load this class with some modifications. The problem is, my @ContextConfiguration is defined on the superclass which is extended by MyTest, and the ApplicationContext is cached between different test classes. Now, if MyTest is run first, it will have the correct PowerMock version of MyComponent, but if not - the test will fail since the context will be loaded for another test (without @PrepareForTest).

So what I want to do is to reload my context before MyTest. I can do that via

@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)

But what if I also want to reload context after this test is done? So I will have clean MyComponent again without PowerMock modifications. Is there a way to do both BEFORE_CLASS and AFTER_CLASS?

For now I did it with the following hack:

@FixMethodOrder(MethodSorters.NAME_ASCENDING)

on MyTest and then

/**
 * Stub test to reload ApplicationContext before execution of real test methods of this class.
 */
@DirtiesContext(methodMode = DirtiesContext.MethodMode.BEFORE_METHOD)
@Test
public void aa() {
}

/**
 * Stub test to reload ApplicationContext after execution of real test methods of this class.
 */
@DirtiesContext(methodMode = DirtiesContext.MethodMode.AFTER_METHOD)
@Test
public void zz() {
}

I am wondering if there is a prettier way to do that?

As a side question, is it possible to reload only certain bean and not full context?

Sam Brannen
  • 29,611
  • 5
  • 104
  • 136
dty
  • 466
  • 2
  • 6
  • 17

1 Answers1

23

Is there a way to do both BEFORE_CLASS and AFTER_CLASS?

No, that is unfortunately not supported via @DirtiesContext.

However, what you're really saying is that you want a new ApplicationContext for MyTest that is identical to the context for the parent test class but only lives as long as MyTest. And... you don't want to affect the context cached for the parent test class.

So with that in mind, the following trick should do the job.

@RunWith(SpringJUnit4ClassRunner.class)
// Inherit config from parent and combine with local 
// static Config class to create a new context
@ContextConfiguration
@DirtiesContext
public class MyTest extends BaseTests {

    @Configuration
    static class Config {
        // No need to define any actual @Bean methods.
        // We only need to add an additional @Configuration
        // class so that we get a new ApplicationContext.
    }
}

Alternative to @DirtiesContext

If you want to have a context dirtied both before and after a test class, you can implement a custom TestExecutionListener that does exactly that. For example, the following will do the trick.

import org.springframework.core.Ordered;
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.AbstractTestExecutionListener;

public class DirtyContextBeforeAndAfterClassTestExecutionListener
        extends AbstractTestExecutionListener {

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }

    @Override
    public void beforeTestClass(TestContext testContext) throws Exception {
        testContext.markApplicationContextDirty(HierarchyMode.EXHAUSTIVE);
    }

    @Override
    public void afterTestClass(TestContext testContext) throws Exception {
        testContext.markApplicationContextDirty(HierarchyMode.EXHAUSTIVE);
    }

}

You can then use the custom listener in MyTest as follows.

import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.TestExecutionListeners.MergeMode;

@TestExecutionListeners(
    listeners = DirtyContextBeforeAndAfterClassTestExecutionListener.class, 
    mergeMode = MergeMode.MERGE_WITH_DEFAULTS
)
public class MyTest extends BaseTest { /* ... */ }

As a side question, is it possible to reload only certain bean and not full context?

No, that is also not possible.

Regards,

Sam (author of the Spring TestContext Framework)

Sam Brannen
  • 29,611
  • 5
  • 104
  • 136
  • Thanks for your reply. `you want a new ApplicationContext for MyTest that is identical to the context for the parent test class` This is not true, because context for MyTest will not be completely the same, since it should use `@PrepareForTest(MyComponent.class)`. I tried your suggestion but encountered another issue. As I understood there will be two contexts in parallel, which is currently not possible (there is a Hazelcast instance configured and there can only be one of those on one port). I can deal with that, so I am curious how to make my Powemock annotation work on this nested context? – dty Sep 02 '16 at 17:10
  • I updated my answer by adding a custom `TestExecutionListener` that dirties the context before and after the test class. Let me know if that solves your problem. – Sam Brannen Sep 10 '16 at 11:23
  • Yep, it does, and looks a lot better than the hack :) Thanks a lot! – dty Sep 14 '16 at 12:47
  • @SamBrannen i have implemented in this fashion and after doing this started getting beanInitializationException with cyclic dependency statement, which is not the case if i don't use the listener in my child classes. on spring testing doc page https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#testcontext-tel-config-automatic-discovery, i see few configration advice, if i am doing for a test suite, i am also using this listener for half of my testSuite, o i need to put a file spring.factory in my jar with the custom listener entry. – Uday Singh May 07 '18 at 19:25
  • @SamBrannen i have implemented in this fashion and after doing error is https://dzone.com/articles/resolve-circular-dependency, which is not the case if i don't use the listener in my child classes. on spring testing doc page https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#testcontext-tel-config-automatic-discovery, i see few configration advice, if i am doing for a test suite, i am also using this listener for half of my testSuite, do i need to put a file spring.factory in my jar with the custom listener entry. – Uday Singh May 07 '18 at 19:48
  • @SamBrannen Is the `@DirtiesContext` annotation on `MyTest` still necessary? – Harold L. Brown May 23 '23 at 18:56