I'm trying to test a class that depends on a bean to work, which I'd like to mock. This bean requires a string value, which is received from my application.yml file using #ConfigurationProperties and that is likely the problem since other beans mocked in the same test class work just fine. Running the application normally also works just fine, so the error seems to be related to the #MockBean in some way.
I have this configuration class which gets the value from the application.yml:
#Data
#ConfigurationProperties("some_api")
public class SomeApiDaoConfig {
private String url;
}
Also, the value is set in the integrationTest application.yml file:
some_api:
url: http://localhost:8082
And also this factory, which creates the bean:
#Factory
public class SomeApiDaoFactory {
#Singleton
public SomeApiDao someApiDao(SomeApiDaoConfig someApiDaoConfig) {
return new SomeApiDao(someApiDaoConfig.getUrl());
}
}
The test class is basically:
#MicronautTest(packages = {"<<path to someApiDao>>"})
public class ServiceTest {
#Inject private BlockingStub blockingStub;
#Inject private AnotherDao anotherDao;
#Inject private SomeApiDao someApiDao;
#BeforeEach
void setUp() {
MockitoAnnotations.initMocks(this);
}
... (tests)
#MockBean(AnotherDao.class)
AnotherDao anotherDao() {
return mock(AnotherDao.class);
}
#MockBean(SomeApiDao.class)
SomeApiDao someApiDao() {
return mock(SomeApiDao.class);
}
When I run the tests, however, this error pops up while it tries to initialize the SomeApiDao bean:
Failed to inject value for parameter [url] of class: <path to test>.$ServiceTest$SomeApiDao3Definition$Intercepted
Message: No bean of type [java.lang.String] exists. Make sure the bean is not disabled by bean requirements (enable trace logging for 'io.micronaut.context.condition' to check) and if the bean is enabled then ensure the class is declared a bean and annotation processing is enabled (for Java and Kotlin the 'micronaut-inject-java' dependency should be configured as an annotation processor).
Path Taken: new GrpcEmbeddedServer(ApplicationContext applicationContext,ApplicationConfiguration applicationConfiguration,GrpcServerConfiguration grpcServerConfiguration,[ServerBuilder serverBuilder],ApplicationEventPublisher eventPublisher,ComputeInstanceMetadataResolver computeInstanceMetadataResolver,List metadataContributors) --> ServerBuilder.serverBuilder(GrpcServerConfiguration configuration,[List serviceList],List interceptors,List serverTransportFilters) --> new Service([SomeApiDao someApiDao]) --> new $ServiceTest$SomeApiDao3Definition$Intercepted([String url],BeanContext beanContext,Qualifier qualifier,Interceptor[] interceptors)
io.micronaut.context.exceptions.DependencyInjectionException: Failed to inject value for parameter [url] of class: <path to test>.$ServiceTest$SomeApiDao3Definition$Intercepted
#Data
#ConfigurationProperties("some_api")
public class SomeApiDaoConfig {
private String url;
}
I'll assume #Data is the Lombok annotation and thus it's creating a constructor argument for the url. Micronaut did not support injecting configuration keys into constructor arguments until 1.3 and it requires the constructor be annotated with ConfigurationInject
Related
AbstractAutoProxyCreator will create the same proxy in getEarlyBeanReference() and postProcessAfterInitialization()
However, MockitoPostProcessor#SpyPostProcessor will create different proxy in either getEarlyBeanReference() or postProcessAfterInitialization(), it will cause circular reference exception in the following code:
#SpringBootTest
public class MockitoTest {
#SpyBean
private DevComponent devComponent;
#Autowired
private TestComponent testComponent;
#Test
public void testMockito() {
// ...
}
}
#Component
public class DevComponent {
#Autowired
private TestComponent testComponent;
}
#Component
public class TestComponent {
#Autowired
private DevComponent devComponent;
}
such as:
Error creating bean with name 'devComponent': Bean with name 'devComponent' has been injected into other beans [testComponent] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.
I am wondering why MockitoPostProcessor#SpyPostProcessor can't create proxy with cache key just like AbstractAutoProxyCreator,which will solve my problem.
I am a complete Spring beginner and I am trying to get basic configuration with Spring Boot working. I would like to use constructor injection wherever possible. However, Spring is throwing exceptions that I do not understand. I've shortened the problematic code for easier reading:
My config YAML file (I have snake yaml on the classpath):
database:
inactive_timeout: 100
active_jdbc_connections:
# this is a list with one property in each element
- name: foo
- name: bar
- name: baz
- name: qux
The application code:
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class DemoApplication {
private final DBProperties dbProperties;
DBProperties getDbProperties() {
return dbProperties;
}
public DemoApplication(DBProperties dbProperties) {
this.dbProperties = dbProperties;
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
And the class that Spring is failing to wire properly:
package com.example.demo;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
#ConfigurationProperties(prefix = "database")
#Component
public class DBProperties {
private final List<ConnectionProperties> activeJdbcConnections;
private int inactiveTimeout;
// ERROR: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'int' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
public DBProperties(List<ConnectionProperties> activeJdbcConnections, int inactiveTimeout) {
this.activeJdbcConnections = activeJdbcConnections;
this.inactiveTimeout = inactiveTimeout;
}
public List<ConnectionProperties> getActiveJdbcConnections() {
return activeJdbcConnections;
}
public int getInactiveTimeout() {
return inactiveTimeout;
}
#Component
public static class ConnectionProperties {
private String name;
// ERROR: Parameter 0 of constructor in com.example.demo.DBProperties$ConnectionProperties required a bean of type 'java.lang.String' that could not be found.
public ConnectionProperties(String name){
this.name = name;
}
public String getName() {
return name;
}
}
}
There are two separate errors. First, in the DBProperties constructor Spring is not able to wire the integer inactiveTimeout, even though it has no trouble wiring the parameter activeJdbcConnections. This can be solved using the #Value parameter, but this is undesirable because #Value does not recognize the prefix specified by #ConfigurationParameters, and so the prefix must be repeated for each #Value annotation.
Second, Spring cannot wire the name parameter of ConnectionProperties. Spring is able to work with a setter if it is added, but as stated above I want to work with constructor injection. As the name of the parameter matches the property I want wired, I do not understand the problem here. As a side note, I was unable to solve this with an #Value annotation, as I do not know the syntax for specifying a property from the current list element.
How can I get Spring to properly instantiate my configuration classes using constructor injection?
There are a few issues with your approach I think. Firstly you can't define beans of type int and String (even if you did try to define them somewhere) so the name and inactiveTimeout will never be available as beans to autowire through constructor injection.
Secondly, #Components when scanned are Spring singleton beans by default which means that there is only one instance of them per Spring context. It doesn't really then make sense for ConnectionProperties to be a component if you're passing it in a list for the DBProperties constructor since there can only be one instatiated ConnectionProperties so it would be a bit of a short list!
If you want DBProperties to be a bean with some parameters provided in its constructor then you probably want to define it as a #Bean directly and call the constructor yourself. Spring can't work out what parameters you want to put in there! Something like:
#Bean
public DBProperties dbProperties() {
...
return new DBProperties(myArray, myNumber);
}
Then this DBProperties bean will be available to be Constructor injected anywhere else in your Spring boot application.
Can not find correct answer to next situation in Internet. So, if it was already discussed somewher, just point out.
I jave UnitTest that is run by Spring:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:spring/app-core-config.xml")
public class ConcurrentProcessingTest extends AbstractLSTest {
public void testMethod1(){
...
}
}
spring/app-core-config.xml contains some beans that use #PropertySource.
For example, Service1Impl:
#Service
#PropertySource("classpath:system/service1.properties")
public class Service1Impl {
#Value("${event.ack.warning}")
private String eventAckWarningComm;
#Value("${event.ack.info}")
private String eventAckInfoComm;
}
So, few classes with similar usage of #PropertySource are defined in spring/app-core-config.xml.
When I run mentioned UnitTest all works fine.
But I need some additional Java configuration for specific UnitTest.
So, I written next simple configuration for previous UnitTest:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {ConcurrentProcessingTest.AppCoreConfiguration.class})
public class ConcurrentProcessingTest extends AbstractLSTest {
#Configuration
#ImportResource("classpath:spring/app-core-config.xml")
//#PropertySource("classpath:system/service1.properties") -- if uncommented, UT works
//But it is annoying to add all propery-files here
static class AppCoreConfiguration {
//Here I want to add extra configuration in Java style
}
public void testMethod1(){
...
}
}
But when I run this UnitTest, I get next exception:
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'event.ack.warning' in string value "${event.ack.warning}"
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:174)
at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126)
at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:194)
at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:158)
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer$2.resolveStringValue(PropertySourcesPlaceholderConfigurer.java:175)
at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:800)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:871)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:858)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:480)
... 184 more
Could someone explain me why #PropertySource is not working in bean defined in XML imported by #ImportResource ?
After you have demarcated your class with respective propertysource you need to declare static propertySourcesPlaceHolderConfigurer bean like below in your configuration class
#Bean
public static PropertySourcesPlaceholderConfigurer xxxpropertyConfig() {
return new PropertySourcesPlaceholderConfigurer();
}
I am trying to write up an integration test case with Spring Boot Test.
I customize the ConversionService to know about the new java.time types:
#Configuration
public class ConversionServiceConfiguration {
#Bean
public static ConversionService conversionService() {
final FormattingConversionService reg = new DefaultFormattingConversionService();
new DateTimeFormatterRegistrar().registerFormatters(reg);
return reg;
}
}
and then later expect it to work:
#Component
class MyServiceConfig {
#Value("${max-watch-time:PT20s}")
private Duration maxWatchTime = Duration.ofSeconds(20);
}
When running under the normal SpringApplication.run this seems to work fine. However, in my test case:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT, classes= {
MyServiceMain.class,
AttachClientRule.class
})
public class MyTest {
#Inject
#Rule
public AttachClientRule client;
#Test(expected=IllegalArgumentException.class)
public void testBad() throws Exception {
client.doSomethingIllegal();
}
}
it blows up:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'AttachClientRule': Unsatisfied dependency expressed through constructor parameter 0:
Error creating bean with name 'MyServiceConfig': Unsatisfied dependency expressed through field 'maxWatchTime': Failed to convert value of type [java.lang.String] to required type [java.time.Duration];
nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [java.time.Duration]: no matching editors or conversion strategy found;
Peering deep into the guts of the TypeConverterDelegate that does the actual conversion, it seems to capture the ConversionService used from a field on the DefaultListableBeanFactory. Setting a watchpoint on where that field is set, I find the AbstractApplicationContext.refresh() method:
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh(); // <--- MyServiceConfig initialized here
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory); // <--- DefaultListableBeanFactory.conversionService set here!!!
// Last step: publish corresponding event.
finishRefresh();
So the #Value injection is happening before the ConversionService is applied to the BeanFactory. No bueno!
I've found what seems to be a workaround:
#Configuration
public class ConversionServiceConfiguration implements BeanFactoryPostProcessor {
#Bean
public static ConversionService conversionService() {
final FormattingConversionService reg = new DefaultFormattingConversionService();
new DateTimeFormatterRegistrar().registerFormatters(reg);
return reg;
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.setConversionService(conversionService());
}
}
This forces the initialization to happen earlier on, but doesn't feel like the right solution (at least it's not documented as such).
Where have I gone wrong?
Spring 4.3.0, Spring Boot 1.4.0M3
EDIT
And now I've discovered another way for it to fail! Without making the same configuration class implement EnvironmentAware:
#Override
public void setEnvironment(Environment environment) {
((AbstractEnvironment) environment).setConversionService(conversionService());
}
I find that the PropertySourcesPropertyResolver uses the wrong (default) ConversionService. This is driving me mad!
Caused by: java.lang.IllegalArgumentException: Cannot convert value [PT15s] from source type [String] to target type [Duration]
at org.springframework.core.env.PropertySourcesPropertyResolver.getProperty(PropertySourcesPropertyResolver.java:94)
at org.springframework.core.env.PropertySourcesPropertyResolver.getProperty(PropertySourcesPropertyResolver.java:65)
at org.springframework.core.env.AbstractPropertyResolver.getProperty(AbstractPropertyResolver.java:143)
at org.springframework.core.env.AbstractEnvironment.getProperty(AbstractEnvironment.java:546)
at com.mycorp.DoSomething.go(DoSomething.java:103)
The Spring Boot developers have confirmed that this is poorly documented and does not work as specified: https://github.com/spring-projects/spring-boot/issues/6222
Try to remove static keyword from conversionService bean definition.
The following example shows explicit wiring of dependencies using spring java config that results in a different bean being wired in while using and interface for a spring configuration class.
This seems like it shouldn't occur or at least give the normal warning that there are two beans as candidates for autowiring and it doesn't know which to select.
Any thoughts on this issue? My guess is there is no real name spacing between configuration classes as is implied by the syntax "this.iConfig.a()" Could this be considered a bug (if only for not warning about the 2 candidate beans)?
public class Main
{
public static void main( final String[] args )
{
final ApplicationContext context = new AnnotationConfigApplicationContext( IConfigImpl.class, ServiceConfig.class );
final Test test = context.getBean( Test.class );
System.out.println( test );
}
}
public class Test
{
private final String string;
public Test( final String param )
{
this.string = param;
}
public String toString()
{
return this.string;
}
}
#Configuration
public interface IConfig
{
#Bean
public String a();
}
#Configuration
public class IConfigImpl implements IConfig
{
#Bean
public String a()
{
return "GOOD String";
}
}
#Configuration
public class ServiceConfig
{
#Autowired
IConfig iConfig;
#Bean
Test test()
{
return new Test( this.iConfig.a() );
}
#Bean
String a()
{
return "BAD String";
}
}
In this case, I would expect to have "GOOD String" to be always be wired in the Test object, but flipping the order of IConfigImpl.class, ServiceConfig.class in the context loader changes which string is loaded.
Tested with Spring 4.0.7
EDIT: Further testing shows this has nothing to to with inherented configs. Same thing results if you drop the IConfig interface.
I believe this was a behavior of Spring for years.
If you redefine a bean, the one that is being loaded as last wins.
Another question would be how to control the order of bean loading when java configs are used. Check out this article http://www.java-allandsundry.com/2013/04/spring-beans-with-same-name-and.html which shows you how to do the ordering by using #Import of the other Spring java config.
The solution is actually simple - if you need to override a previously
defined bean(without say the flexibility of autowiring with a
different bean name), either use the XML bean configuration for both
the bean being overridden and the overriding bean or use the
#Configuration. XML bean configuration is the first example in this
entry, the one with #Configuration would be something like this:
#Configuration
public class Context1JavaConfig {
#Bean
public MemberService memberService() {
return new MemberSvcImpl1();
}
}
#Configuration
#Import(Context1JavaConfig.class)
public class Context2JavaConfig {
#Bean
public MemberService memberService() {
return new MemberSvcImpl2();
}
}
Stepan has mentioned the issue of order. The following is about your comment on their answer
Overriding beans of the same name makes sense, but in this case, I'm
specifically referencing the bean as specified in the iConfig
configuration. I would expect to get the one specified there.
In order to implement #Configuration and the caching of beans so that calls like
#Configuration
class Example {
#Bean
public UncaughtExceptionHandler uncaughtExceptionHandler() {
return (thread, throwable) -> System.out.println(thread + " => " + throwable.getMessage());
}
#Bean
#Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Thread newThread() {
Thread thread = new Thread();
thread.setUncaughtExceptionHandler(uncaughtExceptionHandler()); // <<<<<< allowing this
return thread;
}
}
Spring actually uses CGLIB to create a proxy subtype of the #Configuration annotated class. This proxy maintains a reference to the backing ApplicationContext and uses that to resolve a bean.
So the call in your example
return new Test(this.iConfig.a());
isn't really invoking IConfigImpl#a(). It invokes this code (as of 4.2) from the proxy interceptor. The code uses the corresponding Method to determine the target bean name and uses the ApplicationContext's BeanFactory to resolve the bean. Since the bean definition for a bean named a has already been overriden, that new bean definition gets used. That bean definition is using the ServiceConfig#a() method as its factory method.
This is described in the documentation, here
All #Configuration classes are subclassed at startup-time with CGLIB.
In the subclass, the child method checks the container first for any
cached (scoped) beans before it calls the parent method and creates a
new instance.
Could this be considered a bug [...]?
I don't believe so. The behavior is documented.