I have a suite of integration tests, all configured as:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader = MyLoader.class, classes = {MyConfig.class})
#DirtiesContext
1 of my tests continually fails, complaining about the applicationContext state, throwing the message "Ensure that the context has not been closed programmatically"
As I have a brute force approach of assigning DirtiesContext to every test, really stumped as to what is happening.
You should try and implement a classMode and the DirtiesContext at the class level -
#DirtiesContext(classMode=ClassMode.AFTER_CLASS)
public class TestClass { ..
#Test
public void test1(){ .. }
..
}
as mentioned in Dirties Context JavaDoc so that the context will get closed once all the tests are executed.
#DirtiesContext may be used as a class-level and method-level
annotation within the same class or class hierarchy. In such
scenarios, the ApplicationContext will be marked as dirty before or
after any such annotated method as well as before or after the current
test class, depending on the configured methodMode() and classMode().
Related
I have the following test class:
#SpringBootTest
public class ChoreControllerTest
{
#Autowired
private ChoreController controller;
#Test
public void throwOnMissingChore()
{
assertThrows(ChoreNotFoundException.class, () -> this.controller.getChore(0L));
}
}
It takes about 5 seconds for Spring Boot to start up so the test can run. I want to reduce this time, but if I just remove the #SpringBootTest annotaton, I just get a NPE.
Is there a way to make this controller test more lightweight, or am I stuck with the startup time? I'm especially worried about what will happen to my test times if I ever want to test more than one controller....
The #SpringBootTest annotations create a Spring Context for you therefore it takes a while to start up. This annotation is mostly used for integration tests where a Spring context is required. Here are a few tips for optimizing integration tests.
If you remove the annotation the ChoreController cannot be autowired (no context available) which results in a NullpointerException.
Depending on your needs you can just use a Mocking library like Mockito to inject mocks e.g. services that your controller class needs and run the test without the #SpringBootTest.
You might want to take a look at this article for setting up those mocks properly.
I have been working with Spring and I came across the #DirtiesContext annotation provided by spring. Using it on every method makes sense when you want to refresh the application context before running each method(of course at the expense of performance) but what does it exactly mean to annotate a test class with #DirtiesContext? An example would be really helpful.
If you annotate a test class with #DirtiesContext, it will remove the application context after the tests are run. There is a lot more explaining in this article: DirtiesContext annotation
You can tell the Spring junit runner when to remove the application context with the classMode attribute.
For example if you mark a class with:
#DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
Then the application context will be cleared after each test method. You have more options also.
Other options for the class mode are the following:
AFTER_CLASS: The associated ApplicationContext will be marked as dirty after the test class.
AFTER_EACH_TEST_METHOD: The associated ApplicationContext will be marked as dirty after each test method in the class.
BEFORE_CLASS: The associated ApplicationContext will be marked as dirty before the test class.
BEFORE_EACH_TEST_METHOD: The associated ApplicationContext will be marked as dirty before each test method in the class.
Is there a way to force the full rebuild of Spring context before the test?
Marking it with #DirtiesContext does not affect current test.
WHY: Cause before the test I set some System properties, that influence the context during build.
Sorry, I don't think so.
I haven't tried this, but one workaround might be to obtain the context in the #Before or #BeforeClass method, and call reload.
Your test should obtain the context (and cast it to ConfigurableApplicationContext), by following the test context guidance in the Spring Framework Reference.
Well, I managed to do this... But I warn you, it is dirty!
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { .... })
// order of test methods is important!
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class MyTest {
static {
System.setProperty("myprop", "myvalue");
}
/**
* Fake test that forces spring to reload context before real tests.
*/
#Test
#DirtiesContext
public void aaReloadContext() {
}
/**
* Fake test that clears system property after tests.
*/
#Test
public void zzClearProperty() {
System.clearProperty("myprop");
}
... real test methods...
I'm working on an application which has both component and integration tests. The differences between then is: a component test test more than one class (i.e., its inner objects aren't all mocked out - but some of them might be [such as JMS publishers]) and an Integration test is a test that nothing at all is mocked out. In another words, Spring gives you the object and you test it as it is.
So far, so good.
The problem is: in order to be able to replace one dependency or another from the Spring context, I used Springockito (https://bitbucket.org/kubek2k/springockito/wiki/Home) which offers you a way to mock out some bean from the Spring context.
So - in the component tests - I have this:
#RunWith(SpringJUnit4ClassRunner.class)
#DirtiesContext(classMode = AFTER_CLASS)
#ContextConfiguration(loader = SpringockitoContextLoader.class, locations = "classpath:spring-classify-test.xml")
public class....
#Autowired
#ReplaceWithMock
private SomeServiceInterface someServiceInterface;
#Autowired
private Bean bean;
Bean has SomeServiceInterface as a depedency.
public class Bean {
private SomeServiceInterface...
In the case above, SomeServiceInterface will be replaced by a mock. Of course, that example is an oversimplification of the problem - I replace bean with mock objects that are dependecies further down in the object graph.
It's worthy noticing that I load the context from this file: spring-classify-test.xml Also it's wothy noticing that I mark the context as dirty after the execution of the class - so, AFAIK, the next test class must reload the context.
Now the integration test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader = SpringockitoContextLoader.class, locations = {"classpath:/spring-service-integration-test.xml" })
public class ...
#Autowired
private Bean bean;
I load the context from spring-service-integration-test.xml - but SomeServiceInterface inside of Bean is still mocked! The context used in the integration test was changed as well!
If I mark the Integration test with #DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD), the first test in the class will fail because SomeServiceInterface is mocked - but the next test will pass because the context has been refreshed already.
Funny thing is:
If I ask Spring to inject SomeServiceInterface in the Integration Test, it will inject a SomeServiceInterface concrete implementation - not a mock!
I have tried many things to sort out that issue:
Programatically override the beans in the context after the component tests are done using the registerBeanDefinition method from the context
Create a TestExecution listener so I could try to manually refresh the context before the execution of an IntegrationTest
Use the same loader for the different contexts....
This story goes on and on.
Does anyone have any idea?
P.S.: I quite understand that adopting Springockito was a dubious idea - but that decision was not made by me and now we have over 500 tests in the project - hence refactoring them all to remove Springockito will be a lenghty task, therefore it is not a viable option ATM.
The #DirtiesContext annotation is handled by the DirtiesContextTestExecutionListener when it's registered with the TestContextManager. With plain vanilla Spring tests, that listener is registered by default. Perhaps Springockito or something else in your test "component test" is doing something that interferes with the default listener registration?
I suggest to try latest springockito-annotations 1.0.8 with
#DirtiesMocks(classMode = DirtiesMocks.ClassMode.AFTER_CLASS)
See https://bitbucket.org/kubek2k/springockito/wiki/springockito-annotations#!reseting-mocksspies-in-experimental-package-hope-youll-like-it.
I have a bean that describes my session factory. I only want to load this bean once throughout my entire test suite. I wanted to know, is this possible? And I would like to localize to my context.xml if that is possible too. I can post any source you want, just ask.
Thanks in advance!
In Spring test framework, if all test case classes use the same locations attribute, the context is preserved between runs. In the example below context defined in context.xml will be loaded only once (before first test case) and will be closed when JVM exits:
#ContextConfiguration(locations = "classpath:context.xml")
#Transactional
public class FooTest {
//...
}
#ContextConfiguration(locations = "classpath:context.xml")
#Transactional
public class BarTest {
//...
}
You cannot preserve only one bean, but you can load the whole context once. See my article Speeding up Spring integration tests.