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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With