Load bean once throughout test suite - java

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.

Related

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

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.

Are there any use cases when it is acceptable to use Spring beans created by constructor call?

I am pretty new to Spring and I study using "Spring in Action" (fourth edition) by Craig Walls. The interest is not only on how to write code that is working, but also on the correct principles of using Spring.
Regarding the following piece of code from page 142, Listing 5.6:
public class HomeControllerTest {
#Test
public void testHomePage() throws Exception {
HomeController controller = new HomeController();
MockMvc mockMvc = standaloneSetup(controller).build();
mockMvc.perform(get("/")).andExpect(view().name("home"));
}
}
My questions are generated by the following thoughts:
The general understanding is that Spring uses Dependency Injection as a way to reduce the management overhead of object dependencies, increase modularity, simplify testing and code reuse. However, doesn't it imply that beans must be created and managed by the container? Since I started reading on the subject, the first detail that I memorized stated that new should never appear in a well-written piece of code that follows DI.
Could this be a solution in case we want to test a Stateful bean? I mean, if there are multiple independent tests to be run on the same instance, each of them testing the same state of the bean. Even though I found out that there is a suitable annotation for doing this (#DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)).
Is there another use case that is difficult or impossible to solve otherwise (except for using new)?
A more 'to the letter' implementation would use #ContextConfiguration to specify the ApplicationContext.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = WebConfig.class)
#WebAppConfiguration
public class HomeControllerTest {
#Autowired
HomeController controller;
#Test
public void testHomePage() throws Exception {
MockMvc mockMvc = standaloneSetup(controller).build();
mockMvc.perform(get("/")).andExpect(view().name("home"));
}
}
yes, you shouldn't use new to create Spring bean instances (otherwise they're not Spring beans anymore) in production code. But the whole point of DI is to let you create and manually inject your objects with fake dependencies in unit tests. So the test code here is perfectly fine.
Yes, each unit test is free to create its own bean instance and to populate it the way it needs to. Stateful beans are extremely rare, though. They're usually stateless.
Another place where using new to create a Spring bean is precisely in #Bean-annotated methods of configuration classes. The whole point of these methods is precisely to create and initialize the Spring beans that will then be used and injected by Spring. But again, using new in unit tests is absolutely fine, and the right thing to do.

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 to inject a Mock in a Spring Context [duplicate]

This question already has answers here:
Injecting Mockito mocks into a Spring bean
(23 answers)
Closed 9 years ago.
I have a test that is using some Spring contexts. In these contexts, a number of beans are declared.
I want the test to use the actual implementation of the beans of the contexts, EXCEPT for one of them, for which I want to use a MOCK.
I tried to make the Test a Configuration component (with #Configuration annotation), but the XML seems to take priority over the #Bean annotation, so it doesn't work, this way:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"context1.xml", "context2.xml", ...})
#Configuration
public class MyTest{
#Inject
private MyTargetBean target;
private AnotherBean myMock = mock(AnotherBean.class);
#Bean
public AnotherBean myMock() { return myMock; }
.....
I know that i can define the Mocks in XML, but for that I would need an extra XML file for each test in which I wish to do this. I want to avoid this complexity.
Is there a way to inject a bean (like a mock) in a context apart than through XML?
Thank you!
Yes, you are on the right track, putting a mock #Bean in a #Configuration class is one approach, and I'll describe my experience:
The trick is that you need to use a different set of .xml files purely for testing which exclude the live versions of those beans.
#ContextConfiguration(locations = {"context1-test.xml", "context2-test.xml", ...})
And the "-test-xml" files go in src/test/resources.
At least that was my experience in doing the same thing. Maybe there is some way to "override" the beans with the mock versions, but as yet I am not aware of it.
I also chose to put the mocks (I had 5 of them) all together in an own configuration:
#Configuration
public class MockServicesProvider {
#Bean
public AnotherBean myMock() { return mock(AnotherBean.class); }
}
Another interesting part of this problem is the common usage of initMocks(this); in the #Before method of your test class.
If the mocks are being used in other places (and they are, that's why you are wiring them up...) then initMocks(this) will blow them away between tests (not literally - just that new mocks will be created and any other mocks wired up in other objects will be "lost").
The solution to this was to call mockito's reset(mockObject) in the #Before method before each test. The same mocks are reset (all the when's and interactions), without creating new mocks.
Note that the Mockito docs for reset say very sternly that this method should not commonly be used, except in the context of mocks being applied via dependency injection, as we are indeed doing in this case :)
Have fun!
It is indeed a duplicate of
Injecting Mockito mocks into a Spring bean
The Springockito-annotation is exactly what I was looking for
https://bitbucket.org/kubek2k/springockito/wiki/springockito-annotations

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.

Categories