I have found something that might be related to (27542) or it is morelikely a separate bug regarding DI of Maps. I would surely like it to be fixed.
Spring: 5.3.18
(Spring Boot: 2.6.6
)
TestNG: 7.5
Java: 11
Demo: Spring 5 + XML context + TestNG = wrong type injected: ServiceA3XmlTest.java
factoryGroupsA_fails
test fails because wrong type is injected. This happens in combination of using:
- XML context definition
- TestNG (... extends AbstractTestNGSpringContextTests)
In our Application we use bare Spring 5. Only thing that is different in the example test demo is that it uses Spring Boot (we don't in this case).
Expected type: Map<String, Map<String, MyServiceFactory>>
Injected type: Map<String, Map<String, Map<String, MyServiceFactory>>>
ServiceA3XmlTest.java:
package test.autowiremap.test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.annotations.Test;
import test.autowiremap.service.MyServiceFactory;
import java.util.Map;
@Test
@ContextConfiguration(locations = {"classpath:context.xml"})
public class ServiceA3XmlTest extends AbstractTestNGSpringContextTests {
@Autowired
@Qualifier("xmlFactoryMap")
private Map<String, Map<String, MyServiceFactory>> factoryGroupsXml;
@Autowired
@Qualifier("xmlFactoryMap")
private Object factoryGroupsXmlObject;
@Test
public void factoryGroupsA_fails() {
// Throws NullPointerException due to different data type
MyServiceFactory myServiceFactory = factoryGroupsXml.get("xmlGroup").get("xmlFactory");
System.out.println(myServiceFactory);
}
@Test
public void factoryGroupsA_working() {
var factoryGroupsXmlObject = (Map<String, Map<String, MyServiceFactory>>) this.factoryGroupsXmlObject;
MyServiceFactory myServiceFactory = factoryGroupsXmlObject.get("xmlGroup").get("xmlFactory");
System.out.println(myServiceFactory);
}
}
factoryGroupsA_working
test shows how to evade this issue.
Test Demo: Test results in CI
Error: Failures:
Error: ServiceA3XmlTest>AbstractTestNGSpringContextTests.run:184->factoryGroupsA_fails:25 NullPointer
Comment From: snicoll
I am trying to get my head around that sample and the fact it injects an Object
that you claim works and a precise generic type that doesn't confuses me.
factoryGroups
has a precise generic type and the context is respecting the type you've specified. Can you clarify?
Comment From: SpiReCZ
I get this in runtime for the test class fields (for factoryGroupsXml the expected type gets wrapped in LinkedHashMap with a key responding to the XML definition).
And to answer your question: It would have seem as the Spring context to honor the specified type:
Comment From: snicoll
Sorry that doesn't answer my question. I have cloned your sample and I ran it.
factoryGroupsXml
is defined as Map<String, Map<String, MyServiceFactory>>
and that is exactly what you get. What is the problem?
Comment From: SpiReCZ
I get injected different type than I want/expect to factoryGroupsXml
:
@Autowired
@Qualifier("xmlFactoryMap")
private Map<String, Map<String, MyServiceFactory>> factoryGroupsXml;
Expected type: Mapthat is 2 maps
Injected type: Mapthat is 3 maps
Comment From: SpiReCZ
@snicoll Were you able to replicate the issue by running the tests? My test fails on NullPointerException due to different injected type:
Comment From: snicoll
Alright I get it now, thanks.
I can reproduce with recent JDK and version so I am adding this to the pile of things to investigate.
Comment From: SpiReCZ
Thanks 👍 , appreciated.
Comment From: snicoll
The problem here is that you're injecting a generic type where the related map has been created through XML, which doesn't really provide the full generic type. For the framework, the type ends up being Map<String, Map<?,?>
There's a fallback in the framework when it can't match the map you're asking for to inject a map of bean names to beans. We'd recommend using a dedicated type rather than injecting maps the way you do.