What does it exactly mean to use #DirtiesContext annotation on a class? - java

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.

Related

How can I inject property source of a bean in test

I am writing unit tests for my services in Spring, Java. I mock all dependencies in a tested class and I instantiate the tested class in a constructor, to which I pass mocked classes. The problem is that the tested class injects properties from .properties file into fields that are inside it (let's say Strings).
I use the standard combination of #PropertySource on a class level and #Value on a field level inside my tested class.
As we know, properties injection fails when class is instantiated through constructor (not as a bean during the Spring Container initialization). How do you deal with such problem?
I've got one solution, though I think it is bad and unsatisfactory, that is:
1. to #Autowire the class under test normally, then replace all its dependencies by using a setter.
I also know about the #TestPropertySource annotation and if I understand correctly, it does not provide a solution and it is only a way to override already existent properties - which is not the case, as we cannot really use any properties.
Thanks for help in advance :)
It is rather straight : in your unit test, inject the property in a String field and create the object under test not in the constructor of the test class but in the hook method invoked after the container has loaded the Spring context.
In JUnit 4, you specify this hook method with #Before and in JUnit 5 with #BeforeEach.
It would give something like :
#RunWith(SpringJUnit4ClassRunner.class)
public class FooTest{
Foo foo;
#Value("${myProp}")
String myProp;
#BeforeEach
public void beforeEach(){
foo = new Foo(myProp);
}
}
Note that to make your test be executed faster you should load from the Spring context only what your test requires : the environment part.

How to tell spring to only load the needed beans for the JUnit test?

A simple question that might have an advanced answer.
The Question:
My question is, is there a way to instantiate only the classes, in your application context, needed for that specific JUnit test ?
The Reason:
My application context is getting quite big. I also do a lot of integration tests so you I guess you would understand when I say that every time I run a test all the classes in my application context get instantiated and this takes time.
The Example:
Say class Foo inject only bar
public class Foo {
#Inject
Bar bar;
#Test
public void testrunSomeMethod() throws RegisterFault {
bar.runSomeMethod();
}
but the application context has beans foobar and bar. I know this is not a vaild application context but rest assure all my code works.
<beans>
<bean id="foobar" class="some.package.FooBar"/>
<bean id="bar" class="some.package.Bar"/>
<beans>
So how do I tell spring to only instantiate Bar and ignore FooBar for the test class foo.
Thank you.
Consider adding default-lazy-init="true" to your spring context xml beans tag (or add lazy-init="true" to those specific beans that take a long time starting up).
This will ensure that only those beans are created that called with applicationContext.getBean(class-or-bean-name) or injected via #Autowired / #Inject into your tests. (Some other types of beans like #Scheduled beans will be created nevertheless but you need to check if that's a problem or not)
(if you use spring Java configuration, add #Lazy to the config files)
Caveat - If there is a bean that is not initialized explicitly with applicationContext.getBean() or injected as a dependency used by the bean obtained by using applicationContext.getBean(), then that bean will NO LONGER be constructed or initialized. Depending upon your application, that can cause things to fail OR not. Maybe you can selectively mark those beans as lazy-init="false"
Yes, we can do that, using context per test case. Prepare a test context xml file with the beans required for your test case.
If you use maven, place the test-context.xml under src/test/resources folder.
Annotate your required test class with the following annotation
#ContextConfiguration(locations = "classpath:test-application-context.xml")
This helps in loading only specific beans for the test case.
If you have two kinds of test cases, then
#Runwith(SpringJUnit4Runner.class)
#ContextConfiguration(locations = "classpath:test-context-case1.xml")
public class TestClassCase1 {}
#Runwith(SpringJUnit4Runner.class)
#ContextConfiguration(locations = "classpath:test-context-case2.xml")
public class TestClassCase2 {}
It's not direct answer, so I'd would not mark as solution. But hope it's helpful.
Generally I see three options.
As VinayVeluri answered nicely. Create separate contexts and launch them in every tests separately.
Create context one time per all tests. Just like here: Reuse spring application context across junit test classes It's a big optimization for testing all tests at once.
Mix those two first points. Create one smaller context only for testing purpose. Mock that, what's never is tested but can throw NPE etc. Like here: Injecting Mockito mocks into a Spring bean to boost up context build. And re-use it like in point 2. One time build for all tests. Personally I'd go with that one.
This one waiting for answer about some kind of smart test runner, which creates minimum needed context per test.

How do you get the setup method in a test (with #Before) to not rollback, even when you have a test method (with #Test) set to rollback?

I'm doing some test data load inside the setup() method.
That is, I want this test data to be setup once and be available for all tests within the class.
However, a transaction test method (annotated with #Test and #Rollback(true), causes the setup method to rollback as well..
Is there a way to make sure that only the test method rolls back and not the setup?
Note :- Can't use #BeforeClass, as I need access to Autowired beans from spring context, which are not available in static context of setup(), if it's annotated as #BeforeClass.
Thanks,
Shekhar
PS : A similar question was asked in stackoverflow but never answered :-
#Rollback(false) not working on #Before using SpringJUnit4ClassRunner
Use #TestExecutionListener instead of #BeforeClass. That way your test context will be loaded before execution. Furthermore, the setup code is externalised an can be reused for other tests.
More informations can be found here: What is the difference between #BeforeClass and Spring #TestExecutionListener beforeTestClass()

Mocked Objects in Context during tests execution

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.

Load bean once throughout test suite

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.

Categories