Replacing a Spring bean that have Autowired dependency during testing - spring

I am trying to get Spring to replace a class that has autowired dependencies with another (test class) that do not have these autowire dependencies, but I always end up with a NoSuchBeanDefinitionException like this:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.experiments.beanreplacement.client.Connection' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
I have created a simplifed example to show my problem.
I have two classes in my client package (Connection.java and TcpClient.java) and two in my application package (MessageSender.java and Scheduler.java)
package com.experiments.beanreplacement.client;
#Component
public class Connection {
public void send(String msg) { System.out.println("Connection send: " + msg); }
...
The TcpClient.java autowires the Connection class:
#Component
public class TcpClient {
#Autowired
Connection connection;
public void send(String msg) {
System.out.println("TcpClient send");
connection.send(START_OF_MESSAGE + msg + END_OF_MESSAGE);
}
}
The MessageSender class use the TcpClient to send messages:
package com.experiments.beanreplacement.application;
#Component
public class MessageSender {
#Autowired
TcpClient client;
public void sendAMessage() {
client.send("Hello world!");
client.send("Bye bye...");
}
}
I have set up a test to run this using JUnit:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"/applicationContext.xml"})
public class MessageSenderTest {
#Autowired
MessageSender messageSender;
#Test
public void testMessageSender() {
messageSender.sendAMessage();
}
}
TcpClientMock class:
package client;
#Primary
public class TcpClientMock extends com.experiments.beanreplacement.client.TcpClient{
#Override
public void send(String msg) {
System.out.println("Mock client send: " + msg);
}
...
applicationContext.xml
<bean class="client.TcpClientMock" name="client" >
</bean>
<context:component-scan base-package="com.experiments.beanreplacement.application">
</context:component-scan>
...
In the applicationContext.xml file, I replace the TcpClient which is autowired in the MessageSender class with another (TcpClientMock).
I have adjusted the component-scan to only look at the path of the MessageSender and TcpClientMock, hoping to avoid having to deal with the autowire dependencies of the original TcpClient and underlying Connection.
However, I still get the "No qualifying bean of type 'com.experiments.beanreplacement.client.Connection' available: expected at least 1 bean which qualifies as autowire candidate." error even though the class using the autowire dependency is not part of the component-scan.
Is there a way to avoid this?

You should be able to Mock your TcpClient bean with #MockBean. In you test add:
#MockBean
TcpClient client;
in your test method you can specify the behavior of the send() method like so:
String msg = "anything";
doAnswer(i -> {
System.out.println("Mock client send: " + msg);
return null;
}
).when(client).send(msg);

Related

Spring Testing Unsatisfied depencency NoSuchBeanDefinitionException

When I try to run my tests they all fail becaouse they can't find the bean of one of my classes.
Here are my codes which are used in the context:
The exception I get is this:
Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'testProtoAdminController' : Unsatisfied dependency expressed through constructor parameter 2; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'TestProtoCopyService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
TestProtoAdminControllerTest
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = TestProtoAdminController.class)
public class TestProtoAdminControllerTest {
//Some used services
#Before
public void setUp() {
authenticatedMockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
#WithMockUser
public void testCopyProto() throws Exception {
authenticatedMockMvc.perform(post("/api/admin/{id}/copy", 1)
.contentType(MediaType.APPLICATION_JSON)
.content(asJson(new TestProtoBaseVo()))).andExpect(status().isOk());
}
//Some more tests which are not important in this case
TestProtoCopyService
#Service
public class TestProtoCopyServiceImpl implements TestProtoCopyService {
//Other services and repositories I have to use.
//Methods
}
TestProtoCopyService
public interface TestProtoCopyService {
#Transactional
void copyTestProto(long testProtoId, String sourceTenant, String targetTenant);
}
TestProtoAdminController
#RestController
#RequestMapping("/*")
public class TestProtoAdminController {
private TestProtoCopyService testProtoCopyService;
public TestProtoAdminController(TestProtoCopyService testProtoCopyService {
this.testProtoCopyService = testProtoCopyService;
}
When using #WebMvcTest Spring will prepare everything to test your web layer. This doesn't mean all your beans are scanned and are part of this test application context and ready to inject.
In general, you usually mock the service class of your controller with #MockBean and then use Mockito to specify its behavoir:
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = TestProtoAdminController.class)
public class TestProtoAdminControllerTest {
#MockBean
private TestProtoCopyService mockedService
// the rest
#Before
public void setUp() {
authenticatedMockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
#WithMockUser
public void testCopyProto() throws Exception {
authenticatedMockMvc.perform(post("/api/admin/{id}/copy", 1)
.contentType(MediaType.APPLICATION_JSON)
.content(asJson(new TestProtoBaseVo()))).andExpect(status().isOk());
}
If you want Spring Boot to bootstrap the whole application context with every bean consider using #SpringBootTest. With this annotation, you can inject any bean to your application. The downside here is that you need to provide the whole infrastructure (database/queues/etc.) for your test.

Spring autowire byType vs constructor ( xml configuration)

I have a query. About "Spring autowire byType vs constructor ( xml configuration)".
Read at multiple places that constructor autowire injection is similar to byType. But while I am testing, In case of ambiguity, constructor autowire behaves like byName (even not exactly), need your input if I am missing any important point.
I have below configuration:
<bean name="customerRepository" class="repository.HibernameCustomerRepositoryImpl"/>
<bean name="customerRepository1" class="repository.EclipselinkCustomerRepositoryImpl"/>
<bean name="customerService" class="service.CustomerServiceImpl" autowire="..."/>
byType output : org.springframework.beans.factory.NoUniqueBeanDefinitionException [Good Expected output]
constuctor output: pankaj [Notice I am not getting NoUniqueBeanDefinitionException and it gives me output for customerRepository why? below is sample code]
[Seems In case of ambiguity it does check for property name and choose bean whose name matching with property name]
Sample code:
public class CustomerServiceImpl implements CustomerService {
private CustomerRepository customerRepository;
public CustomerServiceImpl() {
}
public CustomerServiceImpl(CustomerRepository customerRepository) {
this.customerRepository = customerRepository;
}
// public void setCustomerRepository(CustomerRepository customerRepository) {
// this.customerRepository = customerRepository;
// }
#Override
public List<customer> findAll() {
return customerRepository.findAll();
}
}
Yes, Spring does autowire by name, but not exactly like it does with the "byName" autowire mode.
The Spring Documentation states :
Autowiring by property name. Spring looks for a bean with the same name as the property that needs to be autowired. For example, if a bean definition is set to autowire by name and it contains a master property (that is, it has a setMaster(..) method), Spring looks for a bean definition named master and uses it to set the property.
So, in order to autowire by name the setter method should be present. However, in this case the container matches the bean definition name with the name of the constructor argument to autowire in case of ambiguity.
Example:
Motor.java
package com.chiranth;
public interface Motor
{
public void start();
}
ElectricMotor1.java
package com.chiranth;
public class ElectricMotor1 implements Motor
{
public void start()
{
System.out.println("Motor 1 Started.");
}
}
ElectricMotor2.java
package com.chiranth;
public class ElectricMotor2 implements Motor
{
public void start()
{
System.out.println("Motor 2 Started.");
}
}
TeslaModelX.java
package com.chiranth;
public class TeslaModelX
{
private Motor motor;
public TeslaModelX(Motor electricMotor1)
{
motor=electricMotor1;
}
public void goForward()
{
motor.start();
System.out.println("Going Forward.");
}
}
Spring.xml
<?xml version = "1.0" encoding = "UTF-8"?>
<beans xmlns = "http://www.springframework.org/schema/beans"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="electricMotor1" class="com.chiranth.ElectricMotor1"/>
<bean id="electricMotor2" class="com.chiranth.ElectricMotor2"/>
<bean id="modelX" class="com.chiranth.TeslaModelX" autowire="constructor"/>
</beans>
Test.java
package com.chiranth;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test
{
public static void main(String[] args)
{
ApplicationContext context= new ClassPathXmlApplicationContext("Spring.xml");
TeslaModelX modelX=(TeslaModelX)context.getBean("modelX");
modelX.goForward();
}
}
OUTPUT:
Motor 1 Started.
Going Forward.
Even though the property name doesn't match the bean name in the above example, autowiring is achieved.
Yes, you are right, when there is a ambiguos situation, Spring does Autowiring by Name. You can use #Qualifier to help Spring chose the right implementation based on name.
Check the docs

Autowiring not working in SpringBoot controller

Hi i was trying to work over an existing SpringBoot application. I created a service class and tried to autowire it in the existing controller, but when trying to build, its failing saying bean injection failed.
Cause: org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'controller': Injection of autowired
dependencies failed; nested exception is
org.springframework.beans.factory.BeanCreationException: Could not
autowire field: private
com.disooza.www.card.dispenser.service.FilaPartnerService
com.disooza.www.card.dispenser.controller.CardDispenserController.FilaPartnerService;
nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type
[com.disooza.www.card.dispenser.service.FilaPartnerService] found for
dependency: expected at least 1 bean which qualifies as autowire
candidate for this dependency. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
at
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334)
Following is my controller class:
#Controller
#RequestMapping(value = "/service")
public class CardController {
private static final Logger LOGGER = LoggerFactory.getLogger(CardController.class);
#Autowired
private CardDao dao;
#Autowired
private PaymentService paymentService;
#Autowired
private FilaPartnerService filaPartnerService;
FilaPartnerService is the newly created interface, and rest all autowires are working fine in this controller.
Interesting thing is when I try to place this service class in any other controller it is working fine. Any help over this issue will be appreciated, since I'm stuck with it.
This is my service interface:
#Service
public interface FilaPartnerService {
RetrievePaymentTokenResponse retrieveXXX(SupplierRequest request);
}
This is the implementation class:
#Component
public class FilaPartnerServiceImpl implements FilaPartnerService {
#Autowired
private RestTemplate restTemplate;
#Autowired
private RetrieveRequestBuilder retrieveRequestBuilder;
#Value("${filaPartner.url}")
private String filaServiceUrl;
#Override
public RetrievePaymentTokenResponse retrieveFilaPaymentToken(SupplierTokenRequest request) {
RetrievePaymentTokenResponse tokenResponse = null;
RetrievePaymentTokenRequest paymentServiceRequest = retrievePaymentTokenRequestBuilder.retrievePaymentTokenRequestBuilder(request);
try {
tokenResponse =
restTemplate.postForObject( FilaServiceUrl, paymentServiceRequest, RetrievePaymentTokenResponse.class);
} catch (RestClientException exp) {
//TO-DO return error code
}
return null;
}
}

Spring Boot could not autowire and run

I am unable to run the attached Spring Boot sampler application. It has an AMQP starter, requiring RabbitMQ. Fundamentally, it is a simple application that just sends a message to a RabbitMQ Exchange with a queue bound to it. I get the following error:
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.company.messaging.MessageDeliveryManager com.company.exec.Application.messageDeliveryManager; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.company.messaging.MessageDeliveryManager] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
Application.java
package com.company.exec;
#SpringBootApplication
public class Application implements CommandLineRunner {
#Autowired
MessageDeliveryManager messageDeliveryManager;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
public void run(String... args) throws Exception {
messageDeliveryManager.sendMessage(String message);
}
}
MessageDeliveryManager.java
package com.company.messaging;
public interface MessageDeliveryManager {
void sendMessage(String message);
}
MessageDeliveryImpl.java
package com.company.messaging;
public class MessageDeliveryManagerImpl implements MessageDeliveryManager {
#Value("${app.exchangeName}")
String exchangeName;
#Value("${app.queueName}")
String queueName;
#Autowired
RabbitTemplate rabbitTemplate;
#Bean
Queue queue() {
return new Queue(queueName, false);
}
#Bean
DirectExchange exchange() {
return new DirectExchange(exchangeName);
}
#Bean
Binding binding(Queue queue, DirectExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(queueName);
}
public void sendMessage(String message) {
rabbitTemplate.send(queueName, message);
}
}
I would really appreciate if someone can review and provide a suggestion on what I am doing wrong.
Since you have a package tree like this:
com.company.exec
com.company.messaging
and just use a default #SpringBootApplication, it just doesn't see your MessageDeliveryManager and its implementation. That's because #ComponentScan (the meta-annotation on the #SpringBootApplication) does a scan only for the current package and its subpackages.
To make it worked you should add this:
#SpringBootApplication
#ComponentScan("com.company")
Or just move your Application to the root package - com.company.

Spring JavaConfig + JAX-WS Client

I need to create a webservice client to get sportsdata.
But I'm getting an exception when trying to #Autowired sportsdata.
Exception:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [de.openligadb.schema.SportsdataSoap] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
JavaConfig:
#Configuration
#ComponentScan(basePackages = "com.example", excludeFilters = { #Filter(Configuration.class) })
public class MainConfig {
private #Value("${openligadb.wsdlDocumentUrl}") String wsdlDocumentUrl;
private #Value("${openligadb.endpointAddress}") String endpointAddress;
private #Value("${openligadb.namespaceUri}") String namespaceUri;
private #Value("${openligadb.serviceName}") String serviceName;
#Bean
public JaxWsPortProxyFactoryBean sportsdata() throws MalformedURLException {
JaxWsPortProxyFactoryBean ret = new JaxWsPortProxyFactoryBean();
ret.setWsdlDocumentUrl(new URL(wsdlDocumentUrl));
ret.setServiceInterface(SportsdataSoap.class);
ret.setEndpointAddress(endpointAddress);
ret.setNamespaceUri(namespaceUri);
ret.setServiceName(serviceName);
return ret;
}
#Bean
public static PropertySourcesPlaceholderConfigurer properties() {
PropertySourcesPlaceholderConfigurer ret = new PropertySourcesPlaceholderConfigurer();
ret.setLocation(new ClassPathResource("application.properties"));
return ret;
}
}
And yes: I know of #PropertySource but I need to create a bean for it to use it later in my Controller as well.
It's a FactoryBean interoperability problem with #Configuration. Take a look at this answer for details.
The short version is to add a bean explicitly to your configuration.
#Bean
public SportsdataSoap sportsdataSoap() throws ... {
return (SportsdataSoap) sportsdata().getObject();
}

Resources