Spring boot test: MockedBean conflicts with others - spring

Currently, I've two RestTemplate beans:
#Bean
#Primary
public RestTemplate jwtRestTemplate(
RestTemplateBuilder builder,
JWTService jwtService) {
return builder
.additionalInterceptors(
Collections.singletonList(
new JWTHeaderRequestInterceptor(jwtService)
)
)
.build();
}
#Bean
public RestTemplate rawRestTemplate(RestTemplateBuilder builder) {
return builder.build();
}
The first one is primary and the other one is requested by #Qualifier("rawRestTemplate").
However, I'm mocking a ResTemplate into my tests:
#RunWith(SpringRunner.class)
#SpringBootTest()
public class AuditoryTest {
#MockBean()
private RestTemplate frontOfficeRestTemplate;
#Autowired
private DocumentServiceBackOffice documentService;
DocumentServiceBackOffice constructor is:
public DocumentServiceBackOffice(RestTemplate restTemplate);
I'm getting an exception:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in net.gencat.transversal.espaidoc.backoffice.service.DocumentServiceBackOffice required a single bean, but 2 were found:
- rawRestTemplate: defined by method 'rawRestTemplate' in class path resource [net/gencat/transversal/espaidoc/backoffice/config/BackOfficeConfiguration.class]
- jwtRestTemplate: defined by method 'createMock' in null
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
Message is pretty clear, but I don't quite figure out how to solve that.
Any ideas?

I've solved that using that additional Configuration class:
#TestConfiguration
public static class RestTemplateTestConfiguration {
#Bean("jwtRestTemplate")
#Primary
public static RestTemplate someService() {
return Mockito.mock(RestTemplate.class);
}
}

Related

Unable to mock component that uses RestTemplateBuilder

I have a Springboot application that uses RestTemplate to access a third-party json api.
I have written a Component that has the RestTemplate field which is instantiated using Constructor Injection.
#Service("sample")
public class SampleService {
private RestTemplate restTemplate;
#Autowired
public SampleService(RestTemplateBuilder builder) {
this.restTemplate = builder.build();
}
The service works fine.
I am trying to write test cases for the same.
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest(classes = {Application.class,ServiceTest.ContextConfiguration.class})
public class ServiceTest {
#InjectMocks
#Qualifier("sample")
SampleService sampleService;
#Configuration
static class ContextConfiguration {
#Bean
public RestTemplateBuilder restTemplateBuilder() {
RestTemplateBuilder rtb = mock(RestTemplateBuilder.class);
RestTemplate restTemplate = mock(RestTemplate.class);
when(rtb.build()).thenReturn(restTemplate);
return rtb;
}
}
}
I am getting the below exception on test class startup.
org.mockito.exceptions.misusing.InjectMocksException:
Cannot instantiate #InjectMocks field named 'sampleService' of type 'class com.xxx.xxx.core.SampleService'.
You haven't provided the instance at field declaration so I tried to construct the instance.
However the constructor or the initialization block threw an exception : Cannot invoke "org.springframework.boot.web.client.RestTemplateBuilder.build()" because "builder" is null
How to resolve this?

Could not autowire. No beans of 'InstructionRepository' type found

Trying to create a bean in SpringBoot application, but getting the following error "Could not autowire. No beans of 'InstructionRepository' type found."
InstructionRepository is annotated with #Repository annotation in the jar and is an Interface extending a Spring Data Interface
ScheduleProcessor is a method
When I Try adding the #ComponentScan annotation by passing the base package value, the error goes away BUT, when I boot up the application get the following error
Parameter 0 of constructor in com.xxx.resync.config.AppConfig required a bean of type 'com.xxx.repo.InstructionRepository' that could not be found. Action: Consider defining a bean of type 'com.xxx.repo.InstructionRepository' in your configuration.
#Configuration
#EnableAutoConfiguration
//#ComponentScan(basePackages = {"com.xxx.repo"})
public class AppConfig {
#Value("${pssHttp.connectTimeout:3000}")
private int connectTimeout;
#Bean
public RestTemplate getRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
final HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setConnectTimeout(connectTimeout);
factory.setReadTimeout(connectTimeout);
restTemplate.setRequestFactory(factory);
return restTemplate;
}
#Bean
public ScheduleUpdater getScheduleUpdater() {
return new ScheduleUpdater(true);
}
#Bean
public ScheduleProcessor scheduleProcessor(InstructionRepository instructionRepository, ScheduleUpdater scheduleUpdater) {
return new ScheduleProcessor(instructionRepository, scheduleUpdater);
}
}
InstructionRepository
#Repository
public interface InstructionRepository extends CouchbaseRepository<Instruction, String> {
}
How can we fix the error and be able to boot up the Spring boot application?
Any suggestions appreciated.
You need to add #EnableCouchbaseRepositories to enable repo building eg to AppConfig.

Multiple Spring Configuration files (one per Profile)

I'm a Spring rookie and trying to benefit from the advantages of the easy 'profile' handling of Spring. I already worked through this tutorial: https://spring.io/blog/2011/02/14/spring-3-1-m1-introducing-profile and now I'd like to adapt that concept to an easy example.
I've got two profiles: dev and prod. I imagine a #Configuration class for each profile where I can instantiate different beans (implementing a common interface respectively) depending on the set profile.
My currently used classes look like this:
StatusController.java
#RestController
#RequestMapping("/status")
public class StatusController {
private final EnvironmentAwareBean environmentBean;
#Autowired
public StatusController(EnvironmentAwareBean environmentBean) {
this.environmentBean = environmentBean;
}
#RequestMapping(method = RequestMethod.GET)
Status getStatus() {
Status status = new Status();
status.setExtra("environmentBean=" + environmentBean.getString());
return status;
}
}
EnvironmentAwareBean.java
public interface EnvironmentAwareBean {
String getString();
}
EnvironmentAwareBean.java
#Service
public class DevBean implements EnvironmentAwareBean {
#Override
public String getString() {
return "development";
}
}
EnvironmentAwareBean.java
#Service
public class ProdBean implements EnvironmentAwareBean {
#Override
public String getString() {
return "production";
}
}
DevConfig.java
#Configuration
#Profile("dev")
public class DevConfig {
#Bean
public EnvironmentAwareBean getDevBean() {
return new DevBean();
}
}
ProdConfig.java
#Configuration
#Profile("prod")
public class ProdConfig {
#Bean
public EnvironmentAwareBean getProdBean() {
return new ProdBean();
}
}
Running the example throws this exception during startup (SPRING_PROFILES_DEFAULT is set to dev):
(...) UnsatisfiedDependencyException: (...) nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [EnvironmentAwareBean] is defined: expected single matching bean but found 3: prodBean,devBean,getDevBean
Is my approach far from a recommended configuration? In my opinion it would make more sense to annotate each Configuration with the #Profile annotation instead of doing it for each and every bean and possibly forgetting some variants when new classes are added later on.
Your implementations of EnvironmentAwareBean are all annotated with #Service.
This means they will all be picked up by component scanning and hence you get more than one matching bean. Do they need to be annotated with #Service?
Annotating each #Configuration with the #Profile annotation is fine. Another way as an educational exercise would be to not use #Profile and instead annotate the #Bean or Config classes with your own implementation of #Conditional.

Spring factory - NoUniqueBeanDefinitionException:

I am trying to implement factory pattern to get producer from a list of available ones. While doing it i am getting the below exception. Not able to figure out the issue with the code. Can you please let me know what i am missing.
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.test.interfaces.Producer] is defined: expected single matching bean but found 2: A,B
Please find the code below
public interface Producer<T> {
public void start();
public List<T> produce() throws CEHServiceException;
public void stop();
}
#Component("A")
public class ProducerA extends Producer {
//Autowire Services & Properties
}
#Component("B")
public class ProducerB extends Producer {
//Autowire Services & Properties
}
#Configuration
public class AgentConfiguration {
#Bean
public ServiceLocatorFactoryBean createProducerFactoryBean(){
ServiceLocatorFactoryBean bean = new ServiceLocatorFactoryBean();
bean.setServiceLocatorInterface(ProducerFactory.class);
return bean;
}
}
public interface ProducerFactory {
Producer getProducer(String producerName);
}
#Component
public class AdvancedAgentProcessor {
#Autowired
private ObjectFactory<AdvancedRunnerImpl> runnerFactory;
public void init(){
AdvancedRunnerImpl runner = runnerFactory.getObject();
runner.setProducerName("A");
runner.start();
}
}
#Component
#Scope("prototype")
public class AdvancedRunnerImpl implements Runner {
#Autowired private ProducerFactory producerFactory;
private Producer producer;
private String producerName;
public void start() {
producer = producerFactory.getProducer(this.producerName);
}
}
Full stack tracke
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.test.etl.interfaces.Producer] is defined: expected single matching bean but found 2: A,B
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:365)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:331)
at org.springframework.beans.factory.config.ServiceLocatorFactoryBean$ServiceLocatorInvocationHandler.invokeServiceLocatorMethod(ServiceLocatorFactoryBean.java:377)
at org.springframework.beans.factory.config.ServiceLocatorFactoryBean$ServiceLocatorInvocationHandler.invoke(ServiceLocatorFactoryBean.java:363)
at com.sun.proxy.$Proxy34.getProducer(Unknown Source)
at com.test.runner.AdvancedRunnerImpl.start(AdvancedRunnerImpl.java:54)
at com.test.app.AdvancedAgentProcessor.init(AdvancedAgentProcessor.java:48)
at com.test.app.DataAgentApplication.main(DataAgentApplication.java:25)
Spring does not know which component to autowire. It seems that the problem is in the ProducerFactoryImplementation but we cannot see it.
There are three possible solutions:
Use Qualifiers so you can tell Spring which specific implementation you want.There is an example in StackOverflow
here
Use the Primary annotation (See more here3). That means that in case of ambiguity Spring will give priority to the #Primary annotated component
Autowire a list of beans. Something like:
#Autowired private List<Producer> myAvalilableProducers;
public Producer getByName(name){
for( Producer producer: myAvalilableProducers){
if(producer.getName().equals(name)){ return producer; }
}
throw new RuntimeException("No producer with name " + name " found");
}
This third option more useful when you do not know the specific instance at compile time or if you really want to inject a list of components.
You have two beans that extend Producer. Somewhere you are trying to autowire a Producer. Spring does not know which Producer to use.
This happens when the dynamic proxy is not able to pick the correct Bean. Please check whether this.producerName is null or empty.

Spring #Configuration class needs to be autowired

I've created a Spring #Configuration annotated class and I want to autowire a ResourceLoader to it so that I can use it in one of the #Bean methods to lookup a file given by a String. When I am running the app and initialising the context I get a NPE accessing the autowired field, and in debug mode it is shown as being null/not set. Am I wrong expecting the resourceLoader to be present? Am I wrong asserting the autowiring of the Configuration bean happens before its methods get called? The xml configuration loading this bean is tagged with <context:annotation-config/>
#Configuration
public class ClientConfig {
#Autowired
private ResourceLoader resourceLoader;
public #Bean
String configHome() {
return System.getProperty("CONFIG_HOME");
}
public #Bean
PropertiesFactoryBean appProperties() {
String location = "file:" + configHome() + "/conf/webservice.properties";
PropertiesFactoryBean factoryBean = new PropertiesFactoryBean();
factoryBean.setLocation(resourceLoader.getResource(location));
return factoryBean;
}
}
I'm not sure whether this is a bug or is the expected behavior. Sometimes it worked for me, sometimes didn't. Anyway, there is another way of achieving what you want:
public #Bean PropertiesFactoryBean appProperties(ResourceLoader resourceLoader) {
// resourceLoader is injected correctly
...
}

Resources