Spring test @ContextConfiguration and static context
Solution 1
You are right, your code will produce two application contexts: one will be started, cached and maintained for you by @ContextConfiguration
annotation. The second context you create yourself. It doesn't make much sense to have both.
Unfortunately JUnit is not very well suited for integration tests - mainly because you cannot have before class and after class non-static methods. I see two choices for you:
switch to testng - I know it's a big step
encode your setup/tear down logic in a Spring bean included in the context only during tests - but then it will run only once, before all tests.
There are also less elegant approaches. You can use static
variable and inject context to it:
private static ApplicationContext context;
@AfterClass
public static afterClass() {
//here context is accessible
}
@Autowired
public void setApplicationContext(ApplicationContext applicationContext) {
context = applicationContext;
}
Or you can annotate your test class with @DirtiesContext
and do the cleanup in some test bean:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@DirtiesContext(classMode = AFTER_CLASS)
public abstract class AbstractIntegrationTest {
//...
}
public class OnlyForTestsBean {
@PreDestroy
public void willBeCalledAfterEachTestClassDuringShutdown() {
//..
}
}
Solution 2
Not sure whether you chose any approach here, but I encounter the same problem and solved it another way using Spring test framework's TestExecutionListener
.
There are beforeTestClass
and afterTestClass
, so both equivalent to @BeforeClass
and @AfterClass
in JUnit.
The way I do it:
@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners(Cleanup.class)
@ContextConfiguration(locations = { "/integrationtest/rest_test_app_ctx.xml" })
public abstract class AbstractIntegrationTest {
// Start server for integration test.
}
You need to create a class that extends AbstractTestExecutionListener:
public class Cleanup extends AbstractTestExecutionListener
{
@Override
public void afterTestClass(TestContext testContext) throws Exception
{
System.out.println("cleaning up now");
DomainService domainService=(DomainService)testContext.getApplicationContext().getBean("domainService");
domainService.delete();
}
}
By doing this, you have access to the application context and do your setup/teardown here with spring beans.
Hopefully this help anyone trying to do integration test like me using JUnit + Spring.
Related videos on Youtube
Vic
Updated on August 02, 2022Comments
-
Vic over 1 year
I have the following piece of code for my abstract test class (I know
XmlBeanFactory
withClassPathResource
is deprecated, but it's unlikely to be the case of the problem).@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration public abstract class AbstractIntegrationTest { /** Spring context. */ protected static final BeanFactory context = new XmlBeanFactory(new ClassPathResource( "com/.../AbstractIntegrationTest-context.xml")); ... }
It loads the default test configuration XML file
AbstractIntegrationTest-context.xml
(and then I use autowiring). I also need to use Spring in static methods annotated with@BeforeClass
and@AfterClass
, so I have a separate context variable pointing to the same location. But the thing is that this is a separate context, which will have different instances of beans. So how can I merge these contexts or how can I invoke Spring's bean initialization defined by@ContextConfiguration
from my static context?I have in mind a possible solution by getting rid of those static members, but I'm curious, if I can do it with relatively small changes to the code.
-
Vic over 10 yearsThanks for another solution. As far as I remember, I did chose the Tomasz's approach.
-
Matthew Wise over 5 yearsUsing the @TestExecutionListeners annotation appears to replace the use of the default listeners (so you lose dependency injection for example); any idea how to append to the default set instead?
-
Oliver Hernandez over 5 years@MatthewWise You can specify the merge mode of your listener:
@TestExecutionListeners(value = MyListener.class, mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)
-
Matthew Wise over 5 years@OliverHernandez I must have missed that property, thanks!