No qualifying bean of type 'org.springframework.cloud.stream.binder.test.OutputDestination' available - spring-boot

I am trying to use spring cloud contracts for messaging.
I have a producer and a consumer who is publishing and consuming from rabbitmq.
they are working fine when I run both these spring boot applications.
however, when I try to mvn clean install , generated ContractVerifierTest fails with the error
2022-01-18 14:35:55.982 ERROR 62575 --- [ main] s.StreamOutputDestinationMessageReceiver : Exception occurred while trying to read a message from a channel with name [fraud]
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.cloud.stream.binder.test.OutputDestination' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:351) ~[spring-beans-5.3.14.jar:5.3.14]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342) ~[spring-beans-5.3.14.jar:5.3.14]
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1172) ~[spring-context-5.3.14.jar:5.3.14]
at org.springframework.cloud.contract.verifier.messaging.stream.StreamOutputDestinationMessageReceiver.receive(StreamOutputDestinationMessageReceiver.java:43) ~[spring-cloud-contract-verifier-3.1.0.jar:3.1.0]
at org.springframework.cloud.contract.verifier.messaging.stream.StreamOutputDestinationMessageReceiver.receive(StreamOutputDestinationMessageReceiver.java:55) ~[spring-cloud-contract-verifier-3.1.0.jar:3.1.0]
at org.springframework.cloud.contract.verifier.messaging.stream.StreamOutputDestinationMessageReceiver.receive(StreamOutputDestinationMessageReceiver.java:30) ~[spring-cloud-contract-verifier-3.1.0.jar:3.1.0]
at org.springframework.cloud.contract.verifier.messaging.stream.StreamStubMessages.receive(StreamStubMessages.java:59) ~[spring-cloud-contract-verifier-3.1.0.jar:3.1.0]
at org.springframework.cloud.contract.verifier.messaging.stream.StreamStubMessages.receive(StreamStubMessages.java:31) ~[spring-cloud-contract-verifier-3.1.0.jar:3.1.0]
at org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessaging.receive(ContractVerifierMessaging.java:65) ~[spring-cloud-contract-verifier-3.1.0.jar:3.1.0]
I have spring-cloud-stream, spring-cloud-stream-binder-rabbit, spring-cloud-starter-contract-verifier, spring-cloud-stream:test-binder dependencies in my pom as well.
My spring boot application class is configured like this
#SpringBootApplication
#EnableBinding(Source.class)
public class ProducerApplication {
It is sending the message to the destination topic from tests as well. However, it fails to read it
#Test
public void validate_contract_name() throws Exception {
// when:
trigger(); // this happens fine
// then:
ContractVerifierMessage response = contractVerifierMessaging.receive("destination-name",
contract(this, "contract_name.yml")); // THIS FAILS
assertThat(response).isNotNull();
I have annotated my base test class with these
#SpringBootTest(classes = ProducerApplication.class, webEnvironment = SpringBootTest.WebEnvironment.NONE)
#ExtendWith(SpringExtension.class)
#AutoConfigureMessageVerifier
I have verified that OutputDestination class is available in the classpath.
I have org.springframework.cloud:spring-cloud-stream:test-jar:test-binder:3.2.1:test in the classpath.
spring-boot-starter-parent version is 2.6.2. spring cloud version is 2021.0.0
I have configured spring-cloud-contract-maven-plugin version 3.1.0 to use JUNIT5

Related

Startup error in Spring Cloud Stream after upgrading to Spring Boot 2.6.1 and Spring Cloud 2021.0.0

I just upgraded a Spring Boot application that uses Spring Cloud Stream Kafka producers and consumers to
plugins {
id("org.springframework.boot") version "2.6.1"
...
}
extra["springCloudVersion"] = "2021.0.0"
extra["springCloudStreamVersion"] = "3.2.1"
The applications doesn't start anymore with the following exception:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'functionInitializer' defined in class path resource [org/springframework/cloud/stream/function/FunctionConfiguration.class]: Invocation of init method failed; nested exception is java.lang.ClassCastException: class reactor.core.publisher.MonoPeekTerminal cannot be cast to class reactor.core.publisher.Flux (reactor.core.publisher.MonoPeekTerminal and reactor.core.publisher.Flux are in unnamed module of loader 'app')
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1804)
...
Caused by: java.lang.ClassCastException: class reactor.core.publisher.MonoPeekTerminal cannot be cast to class reactor.core.publisher.Flux (reactor.core.publisher.MonoPeekTerminal and reactor.core.publisher.Flux are in unnamed module of loader 'app')
at org.springframework.cloud.sleuth.instrument.messaging.TraceFunctionAroundWrapper.reactorFluxStream(TraceFunctionAroundWrapper.java:187)
at org.springframework.cloud.sleuth.instrument.messaging.TraceFunctionAroundWrapper.reactorStream(TraceFunctionAroundWrapper.java:120)
at org.springframework.cloud.sleuth.instrument.messaging.TraceFunctionAroundWrapper.doApply(TraceFunctionAroundWrapper.java:97)
at org.springframework.cloud.function.context.catalog.FunctionAroundWrapper.apply(FunctionAroundWrapper.java:47)
at org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry$1.doApply(SimpleFunctionRegistry.java:256)
at org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry$FunctionInvocationWrapper.apply(SimpleFunctionRegistry.java:550)
at org.springframework.cloud.stream.function.FunctionConfiguration$FunctionToDestinationBinder.bindFunctionToDestinations(FunctionConfiguration.java:512)
at org.springframework.cloud.stream.function.FunctionConfiguration$FunctionToDestinationBinder.afterPropertiesSet(FunctionConfiguration.java:418)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1863)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800)
... 16 common frames omitted
Did I miss any upgrade guide or is it a bug?
Producer
#Component
class EventProducer(#Qualifier("eventSink") private val eventProcessor: Sinks.Many<Message<EventReceived>>) {
private val logger = LoggerFactory.getLogger(javaClass)
fun send(event: EventReceived): Mono<EventReceived> {
return Mono.defer {
val message = MessageBuilder.withPayload(event)
.setHeader(MESSAGE_KEY, event.id)
.setHeader(TIMESTAMP, OffsetDateTime.now().toInstant().toEpochMilli())
.build()
logger.info("Sending event {}", event)
while (eventProcessor.tryEmitNext(message).isFailure) {
LockSupport.parkNanos(10)
}
event.toMono()
}.subscribeOn(Schedulers.boundedElastic())
}
Consumer
#Configuration
class MetricConsumer(...) {
private val logger = LoggerFactory.getLogger(javaClass)
#Bean
fun consumeMetricUpdated(): Function<Flux<Message<MetricUpdated>>, Mono<Void>> {
...
}
This looks like a bug in s-c-sleuth. I'll follow up with Marcin about this.
Can you please post the signature of your function as well, need to confirm something?
Meanwhile, you can temporarily disconnect sleuth's TraceFunctionAroundWrapper by setting spring.sleuth.function.enabled to false.

Unable to call Mapper.xml file by using junit testing for the application developed using Mybatis+Springboot

I'm very new to junit testing. How to write junit test real database call from mybatis.xml file.
Please find the below code.
#RunWith(SpringRunner.class)
//#MybatisTest
#SpringBootTest
public class HotelMapperTest {
#Autowired
private HotelMapper hotelMapper;
#Test
public void selectByCityIdTest() {
Hotel hotel = hotelMapper.selectByCityId(1);
assertThat(hotel.getName()).isEqualTo("Conrad Treasury Place");
assertThat(hotel.getAddress()).isEqualTo("William & George Streets");
assertThat(hotel.getZip()).isEqualTo("4001");
}
when i run the junit testing i'm getting below exception:
org.apache.ibatis.binding.BindingException: Invalid bound statement (not found):
Herer my question is how we'll test the real database, When enable the #MybatisTest it's looking for datasource, already we specified all properties in applicaiton.properties. In this time i'm getting below exception:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource':
You can config mybatis mapper location in spring configuration file (such as application.yml).
mybatis configuration:
mybatis:
mapper-locations:
- classpath*:mapper/*.xml

DI in tests without using spring boot (#SpringBootTest)

Switching from spring boot back to "normal" spring because the app only uses some jdbc code to "upsert" into a postgresql database.
1)
tried annotating the test class with:
#RunWith(SpringJUnit4ClassRunner.class)
public class DBIntegration {
results in:
java.lang.IllegalStateException: Failed to load ApplicationContext
2)
tried annotating the class with:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {})
public class DBIntegration {
[main] INFO org.springframework.test.context.support.AbstractContextLoader - Could not detect default resource locations for test class [de.mydomain.myproject.DBIntegration]: no resource found for suffixes {-context.xml}.
No exceptions, but java.lang.Exception: No tests found matching [{ExactMatcher:fDisplayName=insertDataFrom_sometest],
3) tried annotating the class with:
#Component
public class DBIntegration {
dependency injection does not work in this case, the expected service
(to be injected) throws a nullpointerexception

Spring Boot 2.0.0.M2 and Spring Data Elasticsearch configuration

I'm trying to move my project to Spring Boot 2.0.0.M2.
This is my old Spring Data Elasticsearch configuration:
import org.elasticsearch.client.Client;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
#Profile("production")
#Configuration
#EnableElasticsearchRepositories(basePackages = "com.example.domain.repository.elasticsearch")
public class ElasticsearchConfig {
#Value("${elasticsearch.host}")
private String host;
#Value("${elasticsearch.port}")
private int port;
#Bean
public Client client() throws Exception {
return TransportClient.builder().build().addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(host), port));
}
#Bean
public ElasticsearchOperations elasticsearchTemplate() throws Exception {
return new ElasticsearchTemplate(client());
}
}
Right now I faced a following issue:
1. The method builder() is undefined for the type TransportClient
2. InetSocketTransportAddress cannot be resolved to a type
On the Maven classpath I have Spring Data Elasticsearch 3.0.0.M4:
How to properly configure current version of Spring Data Elasticsearch ?
UPDATED
For my tests I use Embedded Elasticsearch with a following application.properties:
#Elasticsearch
spring.data.elasticsearch.properties.http.enabled=true
spring.data.elasticsearch.properties.http.port=9250
spring.data.elasticsearch.properties.path.home=target/test-elasticsearch-db
spring.data.elasticsearch.properties.transport.tcp.connect_timeout=60s
This is my ES test config:
#Profile("test")
#Configuration
#EnableElasticsearchRepositories(basePackages = "com.example.domain.repository.elasticsearch")
public class ElasticsearchTestConfig {
}
Right now the test fails with a following error:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'elasticsearchTemplate' defined in class path resource [org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchDataAutoConfiguration.class]: Unsatisfied dependency expressed through method 'elasticsearchTemplate' parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.elasticsearch.client.Client' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:726) ~[spring-beans-5.0.0.RC2.jar:5.0.0.RC2]
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:458) ~[spring-beans-5.0.0.RC2.jar:5.0.0.RC2]
and
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.elasticsearch.client.Client' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1478) ~[spring-beans-5.0.0.RC2.jar:5.0.0.RC2]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1089) ~[spring-beans-5.0.0.RC2.jar:5.0.0.RC2]
What is wrong here and how to fix this ?
Spring Boot 2.0 uses Elasticsearch 5 which includes some breaking API changes. You would be shielded from those changes if you used Spring Boot's auto-configuration rather than trying to write your own.
All that's needed is a value for the spring.data.elasticsearch.cluster-nodes property. With that in place, Spring Boot will auto-configure both a TransportClient and an ElasticsearchTemplate.
Had similar issue when migrating from spring-boot 1.5.6 to 2.0.0. The reason seems to be previous support for embedded elasticsearch is not more supported and the same is reflected in spring-boot.
Previously leaving the following property empty
spring.data.elasticsearch.cluster-nodes
put the following into use
spring.data.elasticsearch.properties.path.home
and spring-boot created given directory in the target folder for embedded mode to run. With spring-boot 2.0.0 (spring-boot-autoconfigure-2.0.0.RELEASE.jar to be precise) cluster-nodes has become asserted for non-null value causing elasticsearchTemplate bean not to be created under the hood.
That is the reason why your app stop working so suddenly.

Spring Boot ConfigurationProperties fail to initialize for integration testing

Using gradle (3.4.1) with an integrationTest configuration, the tests using the ConfigurationProperties of Spring Boot (1.5.1.RELEASE) is failing to initialize even though the application initializes correctly (./gradlew bootRun). The class annotated with ConfigurationProperties is similar to the following
#Component
#ConfigurationProperties(prefix = "foo")
#Validated
public class AppConfiguration {
#NonNull
private URL serviceUrl;
...
The configuration file does have getters and setters. The error that is generated is similar to the following
java.lang.IllegalStateException: Failed to load ApplicationContext
....
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'AppConfiguration': Could not bind properties to AppConfiguration
....
Caused by: org.springframework.validation.BindException: org.springframework.boot.bind.RelaxedDataBinder$RelaxedBeanPropertyBindingResult
Field error in object 'foo' on field 'serviceUrl': rejected value [null]; codes ...
The configuration class of the integration test is annotated as follows
#Configuration
#ComponentScan(...)
#EnableConfigurationProperties
#EnableIntegration
public static class ContextConfiguration {}
The test class had the following annotations
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class ReleaseTest {
...
After looking at the Spring Boot code for the ConfigurationPropertiesBindingPostProcessor#postProcessBeforeInitialization() it suggested that the property source was not being discovered. Adding the org.springframework.boot:spring-boot-starter-test artifact as a compile-time dependency and modifying the context configuration of the test class to
#ContextConfiguration(initializers = ConfigFileApplicationContextInitializer.class)
the AppConfiguration class was initialized properly using a YAML-based properties file.
An alternative is to add
#TestPropertySource("classpath:/application.properties")
This approach doesn't require the spring-boot-starter-test dependency and requires that a "traditional" properties file is used (a YAML file will not work with this approach).

Resources