Spring auto populate list with mocks - spring

I would like to know if anybody has successfully #Autowired an auto-populated list of objects, injecting mocks, with Spring during the test phase of the build? What I want to be able to do is override Spring's auto-population of a list during test time and have it populated with mocks within a unit test, instead of the implementation classes. I have successfully accomplished this by specifying #Resource within the code instead of #Autowired, but then when I deploy the Spring web app, the auto-population does not execute with #Resource specified for my list (it's just empty). The oppposite happens when I specify #Autowired on the list. The list is auto-populated when the app runs, but then I cannot populate the list with mocks when the unit tests run. It seems to be a catch-22...
So how do I use #Resource on a List type and have Spring still do the auto-population at runtime? Has anybody done this successfully - use auto-population at runtime, but substitute mocks into the list during the test phase? If so, could you possibly post relevant parts of your test #Configuration class? (Java annotations please, not XML). Thanks..
This works for injecting mocks, but then auto-population of the list doesn't kick in at runtime:
#Resource(name = "myServices")
private List<MyService> myServices;
And in my test config:
#Bean
#Qualifier("myServices")
public List<MyService> myServices() {
List<MyService> myServices = new ArrayList<>();
MyService mockService1 = Mockito.mock(MyService.class, Mockito.RETURNS_DEEP_STUBS);
MyService mockService2 = Mockito.mock(MyService.class, Mockito.RETURNS_DEEP_STUBS);
eventServices.add(mockService1);
eventServices.add(mockService2);
return myServices;
}
And then with the following, auto-population is active all the time (at runtime and during the test phase), but I cannot override and inject the mocks during the test phase with #Autowired, as it ignores the myServices #Bean definition from the test config:
#Autowired
#Qualifier("myServices")
private List<MyService> myServices;
Thanks in advance for any insight on this.

Related

Mocking repository / filling db BEFORE container start for app-startup integration test

I'd like to write integration test to verify proper function of #EventListener(ApplicationReadyEvent.class) annnotated method, but I need to setup the data to be found by some repository. The #SQL is executed only before #Test method, ie. after the ApplicationReadyEvent was processed., #MockBean followed by Mockito.when in #Before method is the same, it's reached only after #SpringBootTest finished initialization, namely after ApplicationReadyEvent was processed.
I also tried to provide #TestConfiguration with bean defined as:
#Bean
SomeRepository someRepository() {
SomeRepository someRepository = Mockito.mock(SomeRepository.class);
Mockito.when(someRepository.findById("value")).thenReturn(...);
return someRepository;
}
but this is somewhat insufficient, as the #SpringBootTest still finds actual implementation of this repository (even if I add #AutoConfigureMockMvc) and run crashes with this conflict.
How can I prepare the mocked repository or actual data in test db for this scenario?

Spring boot test minimal test slice or manual configuration

I have many different SpringBoot tests running. So far the auto configuration slices were really helpful, especially in combination with #MockBean.
But in my current test no such slice fits and booting up the complete context using #SpringBootTest is too slow.
Is there a way to manually set the tip of the object tree to be started with and from there spring autowires all needed beans? Or is there a way to set all needed beans manually?
In my specific case i want to test a MapStruct generated mapper (using componentModel = "spring") this mapper uses two other mappers, each injecting a service to do their work.
The services are provided via #MockBean:
#RunWith(SpringRunner.class)
#SpringBootTest
public class ProductResponsibleUnitMapperTest {
#Autowired
private PRUMapper mapper;
#MockBean
private TradingPartnerService tradingPartnerService;
#MockBean
private ProductHierarchyService productHierarchyService;
#Test
public void mapForthAndBack(){
//works but takes ages to boot
}
}
I could not use constructor injection on the mappers (for the services) because MapStruct won't generate correct implementations.
How to get a Spring-Context only containing the needed beans?
I found one way by explicitly declaring all implementation used:
#SpringBootTest(classes = {ProductResponsibleUnitMapperImpl.class, LegalEntityMapperImpl.class, ProductHierarchyMapperImpl.class})
For more complex setups it will be cumbersome and also dangerous to declare generated classes.
I am still searching for a better cleaner way to let Spring decide what classes needed. It should be possible to set the class in hand and let Spring decide what classes needed and to be instantiated.

Multiple tests with autowired MockHttpServletRequest not working?

I use an #Autowired MockHttpServletRequest in some of my Spring tests. TestNG is used as testing framework. If I only have one test method in the class this works fine. However, if there are multiple test methods, only the first run test uses my MockHttpServletRequest. Let me illustrate with an example:
#WebAppConfiguration
#ContextConfiguration({"classpath:applicationContext.xml"})
public class FooTest extends AbstractTestNGSpringContextTests {
#Autowired
private MockHttpServletRequest servletRequest;
#Test
public void test1() {
assertEquals(((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest(), servletRequest);
}
#Test
public void test2() {
assertEquals(((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest(), servletRequest);
}
}
In this example, test1() passes, but test2() fails! If you run the test methods individually, they both pass. Why does one test fail if they are run together?
I tried to dig in the code, there seems to be some kind of reset of the request attributes after a test method have run, but I didn't find a way to turn it off. My Spring version is 3.2.8.RELEASE.
UPDATE: This has been fixed in Spring Framework 3.2.9, 4.0.4, and 4.1. See SPR-11626 for details.
Well, my friend... you have discovered a bug in the Spring TestContext Framework.
The reason for this behavior is that ServletTestExecutionListener resets the request attributes after each test method, but DependencyInjectionTestExecutionListener does not re-inject dependencies before each test method (by default). When the second test method is executed, the servletRequest field is still referencing the MockHttpServletRequest that was created for the previous test method; whereas, ServletTestExecutionListener creates a new instance of MockHttpServletRequest for each test method and sets it in the request attributes. Thus, the injected request and the one stored in the RequestContextHolder are only the same for the first test method that executes in TestNG.
Since I am the author of this code, I have to personally apologize, but... I'll make sure it gets fixed ASAP. See SPR-11626 for details on the status of the fix. ;)
Note: this bug only applies to TestNG tests; this does not apply to JUnit tests.
As a work-around, you can annotate the affected test methods with #DirtiesContext (or annotate your test class with #DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)). This will allow your tests to pass as you expect.
The use of #DirtiesContext will make Spring close your test ApplicationContext after each test method, and this will likely have a negative impact on the speed of your tests; however, as of Spring 3.2.8 and 4.0.3, this is the only non-custom solution.
Having said that, the following is a much more efficient work-around. Just define this custom TestExecutionListener in your project:
public class AlwaysReinjectDependenciesTestExecutionListener extends AbstractTestExecutionListener {
public void afterTestMethod(TestContext testContext) throws Exception {
testContext.setAttribute(DependencyInjectionTestExecutionListener.REINJECT_DEPENDENCIES_ATTRIBUTE, Boolean.TRUE);
}
}
And then annotate your test class like this:
#TestExecutionListeners(AlwaysReinjectDependenciesTestExecutionListener.class)
That should clear up any issues and keep your test suite running quickly.
Regards,
Sam

#Autowired gives different object in each time of JUnit test

I have a class named TestedClass that is annotated with #Service("service").
I want to inject this class in my JUnit Test Class.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:/META-INF/spring/applicationContext.xml"})
public class JUnitTest {
#Autowired
TestedClass testedClass;
#Test
public void test() {
System.out.println(testedClass.toString());
}
And then I start my server and run this JUnitTest class.
I think that the output should be same in each time of test without stop the server. But it print different results.
TestedClass#1ed4b47
TestedClass#12f9e9
Why?
If you are running the test multiple times, different instances of the bean will get injected. Once you run the test the first bean will get destroyed. When you run the test again a new instance (singleton) must be made. The test framework will fire up an IOC container once per run of the test, the beans will not persist across multiple runs.

How can a test 'dirty' a spring application context?

The spring framework documentation states:
In the unlikely case that a test may
'dirty' the application context,
requiring reloading - for example, by
changing a bean definition or the
state of an application object -
Spring's testing support provides
mechanisms to cause the test fixture
to reload the configurations and
rebuild the application context before
executing the next test.
Can someone elaborate this? I am just not getting it. Examples would be nice.
Each JUnit test method is assumed to be isolated, that is does not have any side effects that could cause another test method to behave differently. This can be achieved by modifying the state of beans that are managed by spring.
For example, say you have a bean managed by spring of class MySpringBean which has a string property with a value of "string". The following test method testBeanString will have a different result depending if it is called before or after the method testModify.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"/base-context.xml"})
public class SpringTests {
#Autowired
private MySpringBean bean;
#Test public void testModify() {
// dirties the state of a managed bean
bean.setString("newSring");
}
#Test public void testBeanString() {
assertEquals("string", bean.getString());
}
}
use the #DirtiesContext annotation to indicate that the test method may change the state of spring managed beans.

Resources