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?
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)