If I hard code the value for destination, processSampleMessage execution is happening.
If I try to change it to dynamic one ${ibm.mq.channel}, not executing method. Did I miss anything?
#Component
public class SampleMessageReceiver {
#Autowired private RestTemplate restTemplate;
#Autowired private UrlsConfig urlsConfig;
#JmsListener(
destination = "${ibm.mq.channel}",
containerFactory = "myListenerContainerFactory",
selector = "JMSCorrelationID='c9d5e2d7c5c3e3c9d6d54040404040404040404040404040'")
public void processSampleMessage(#Valid SampleMessage sampleMessage) {
System.out.println("~~~~~~~~~~~~~~~~~~~ In process SampleMessages ~~~~~~~~~~~~~~~~~~~\n\n");
}
}
Related
I have the following publishing class.
#Component
public class Publisher {
#Autowired
private MessageChannel publishingChannel;
#Override
public void publish(Event event) {
publishingChannel.send(event);
}
}
I have the following test class.
#RunWith(SpringRunner.class)
#SpringBootTest
public class PublisherTest {
private final List<Event> events = new ArrayList<>();
#Autowired
private Publisher publisher;
#Test
public void testPublish() {
Event testEvent = new Event("some_id_number");
publisher.publish(testEvent);
Awaitility.await()
.atMost(2, TimeUnit.SECONDS)
.until(() -> !this.events.isEmpty());
}
#ServiceActivator(inputChannel = "publishingChannel")
public void publishEventListener(Event event) {
this.events.add(event);
}
}
The message channel bean is instantiated elsewhere. The publisher runs as expected and an event is publishing to the channel, however the service activator is never invoked. What am I missing here?
Turns out you need to move the service activator to a separate test component class (#TestComponent prevents this from being injected outside the test context).
#TestComponent
public class TestListener {
public final List<Object> results = new ArrayList<>();
#ServiceActivator(inputChannel = "messageChannel")
public void listener(Event event) {
Object id = event.getHeaders().get("myId");
results.add(id);
}
}
Then you can bring this listener into your test. Make sure you use #Import to bring your service activator class into the test context.
#SpringBootTest
#Import(TestListener.class)
class PublisherTest {
#Autowired
private Publisher publisher;
#Autowired
private TestListener testListener;
#Test
void testPublish() {
this.publisher.publish(new Event().addHeader("myId", 1));
Awaitility.await()
.atMost(2, TimeUnit.SECONDS)
.until(() -> !this.testListeners.results.isEmpty());
}
}
The test passes after making these changes. Figured this out with a demo app and applied it to a production testing issue.
I'm trying to start up a Pub/Sub setup with Spring Data Redis, and I'm able to load the publisher, but the RedisMessageListenerContainer doesn't start up automatically. I'm using Spring Data Redis 2.2.8.RELEASE along with embedded redis server (it.ozimov.embedded-redis version 0.7.2). Does anyone have any insight as to why the RedisMessageListenerContainer won't start up?
Here are my classes.
RedisListenerAutoConfiguration
#Configuration
#ConditionalOnProperty(prefix = "myproj.redis", name = "mode", havingValue = "LISTENER", matchIfMissing = true)
#ComponentScan("com.jcworx.redis.listener")
#ConditionalOnBean(type = "com.jcworx.redis.listener.RedisMessageListener")
#AutoConfigureAfter(RedisAutoConfiguration.class)
#EnableConfigurationProperties(RedisConfigurationProperties.class)
public class RedisListenerAutoConfiguration {
#Autowired
private RedisConfigurationProperties redisConfigurationProperties;
#Bean
public MessageListenerAdapter messageListenerAdapter(RedisMessageListener<?> redisMessageListener){
return new MessageListenerAdapter(redisMessageListener,"onRedisMessage");
}
#Bean
public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory, MessageListenerAdapter messageListenerAdapter){
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.addMessageListener(messageListenerAdapter, new ChannelTopic(redisConfigurationProperties.getQueueName()));
return container;
}
}
SimpleRedisMessageListener
#Component
public class SimpleRedisMessageListener extends AbstractRedisMessageListener<SimpleType>{
private static final Logger LOG = LoggerFactory.getLogger(SimpleRedisMessageListener.class);
private CountDownLatch countDownLatch;
#Override
public void processRedisMsg(RedisMessage<SimpleType> redisMsg) {
LOG.info("Processing Message. trxId={}, payload={}",redisMsg.getTrxId(),redisMsg.getPayload());
Assert.notNull(countDownLatch,"Count Down Latch cannot be null.");
countDownLatch.countDown();
}
public CountDownLatch getCountDownLatch() {
return countDownLatch;
}
public void setCountDownLatch(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
}
RedisServerConfiguration
#Configuration
#Profile("test")
public class RedisServerConfiguration {
private RedisServer redisServer;
#Autowired //redisProperties autowired from RedisAutoConfiguration
public RedisServerConfiguration(RedisProperties redisProperties){
redisServer = new RedisServer(redisProperties.getPort());
}
#PostConstruct
public void postConstruct(){
redisServer.start();
}
#PreDestroy
public void preDestroy(){
redisServer.stop();
}
}
application-test.properties
#application test resources
myproj.redis.queueName=test
spring.redis.host=localhost
spring.redis.port=6379
#set to true when you need to see the auto configuration rules
debug=true
RedisPubSubABTTest
#SpringBootTest(classes = TestRedisApp.class)
#ActiveProfiles("test")
public class RedisPubSubABTTest {
#Autowired
private RedisMessagePublisher redisMessagePublisher;
#Autowired
private SimpleRedisMessageListener simpleRedisMessageListener;
/**
* Send a message to the embedded redis queue and await the listener to respond. If it
* responds, then the countdown latch will count down to 0. Otherwise, it will time out
* and fail to respond.
* #throws InterruptedException
*/
#Test
public void messageSentAndReceived() throws InterruptedException{
//ARRANGE
SimpleType simpleType = new SimpleType();
simpleType.setFirstName("John");
simpleType.setLastName("Smith");
CountDownLatch countDownLatch = new CountDownLatch(1);
simpleRedisMessageListener.setCountDownLatch(countDownLatch);
RedisMessage<SimpleType> redisMsg = new RedisMessage.Builder<SimpleType>().TrxId(UUID.randomUUID().toString())
.payload(simpleType)
.build();
//ACT
redisMessagePublisher.publish(redisMsg);
boolean responded = countDownLatch.await(5, TimeUnit.SECONDS);
//ASSERT
Assertions.assertTrue(responded);
}
}
As it turns out, The MessageListenerAdapter uses the RedisSerializer.string() as the default serializer. This means that any POJO other than a String in the parameter list of the listener method will be ignored. In order to get past this, you need to invoke the setSerializer method and pass in RedisSerializer.java() as the argument. This will let the MessageListenerAdapter know that the POJO is a java class and needs to be serialized/deserialized. Please note that whatever pojo that you decide to pass in MUST implement java.io.Serializable. Please see the example below, and hopefully this helps someone else.
#Bean
public MessageListenerAdapter messageListenerAdapter(RedisMessageListener<?> redisMessageListener){
MessageListenerAdapter msgAdapter = new MessageListenerAdapter(redisMessageListener,"onRedisMessage");
msgAdapter.setSerializer(RedisSerializer.java());
return msgAdapter;
}
I built a JMS listener to point at an SQS queue, and I've sent messages to the queue, but unfortunately they're not getting picked up off of it. How do I check to make sure the listener is active and ready to accept new messages. For clarity, here's the code with unnecessary pieces stripped out:
#Component
#Conditional(SqsListeningEnabledCondition.class)
public class SqsJobListener {
private static final Logger logger = LoggerFactory.getLogger(SqsJobListener.class);
private final ObjectMapper objectMapper;
private final JobFactoryMap jobFactoryMap;
private final CacheManager cacheManager;
#Autowired
public SqsJobListener() {
}
#JmsListener(destination = "${aws.job.queue}", containerFactory = "customListenerContainerFactory", concurrency = "1")
public void receiveIngestionMessage(String message) {
//deserializing bits here
}
}
I want to write a simple test using #RestClientTest for the component below (NOTE: I can do it without using #RestClientTest and mocking dependent beans which works fine.).
#Slf4j
#Component
#RequiredArgsConstructor
public class NotificationSender {
private final ApplicationSettings settings;
private final RestTemplate restTemplate;
public ResponseEntity<String> sendNotification(UserNotification userNotification)
throws URISyntaxException {
// Some modifications to request message as required
return restTemplate.exchange(new RequestEntity<>(userNotification, HttpMethod.POST, new URI(settings.getNotificationUrl())), String.class);
}
}
And the test;
#RunWith(SpringRunner.class)
#RestClientTest(NotificationSender.class)
#ActiveProfiles("local-test")
public class NotificationSenderTest {
#MockBean
private ApplicationSettings settings;
#Autowired
private MockRestServiceServer server;
#Autowired
private NotificationSender messageSender;
#Test
public void testSendNotification() throws Exception {
String url = "/test/notification";
UserNotification userNotification = buildDummyUserNotification();
when(settings.getNotificationUrl()).thenReturn(url);
this.server.expect(requestTo(url)).andRespond(withSuccess());
ResponseEntity<String> response = messageSender.sendNotification(userNotification );
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
private UserNotification buildDummyUserNotification() {
// Build and return a sample message
}
}
But i get error that No qualifying bean of type 'org.springframework.web.client.RestTemplate' available. Which is right of course as i havn't mocked it or used #ContextConfiguration to load it.
Isn't #RestClientTest configures a RestTemplate? or i have understood it wrong?
Found it! Since i was using a bean that has a RestTemplate injected directly, we have to add #AutoConfigureWebClient(registerRestTemplate = true) to the test which solves this.
This was in the javadoc of #RestClientTest which i seem to have ignored previously.
Test which succeeds;
#RunWith(SpringRunner.class)
#RestClientTest(NotificationSender.class)
#ActiveProfiles("local-test")
#AutoConfigureWebClient(registerRestTemplate = true)
public class NotificationSenderTest {
#MockBean
private ApplicationSettings settings;
#Autowired
private MockRestServiceServer server;
#Autowired
private NotificationSender messageSender;
#Test
public void testSendNotification() throws Exception {
String url = "/test/notification";
UserNotification userNotification = buildDummyUserNotification();
when(settings.getNotificationUrl()).thenReturn(url);
this.server.expect(requestTo(url)).andRespond(withSuccess());
ResponseEntity<String> response = messageSender.sendNotification(userNotification );
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
private UserNotification buildDummyUserNotification() {
// Build and return a sample message
}
}
I am writing Kafka Consumer Unit Test, and need to Mock the Service of my KafkaConsumer for testing the Kafka Consumer independently. But, the mockObject of Service is not getting invoked, instead Spring is creating the original Service class object and calling it. Thus, my mock class object not getting called.
KafkaConsumer :
#Slf4j
#Component
#RequiredArgsConstructor (onConstructor = #__(#Autowired))
public class KafkaEventConsumer {
private final MyService requestService;
#KafkaListener (topics = "${kafka.topic:topic-name}")
public void receive(#Payload String message) throws Exception {
try {
LOGGER.debug("Received message:{} ", message);
ObjectMapper mapper = new ObjectMapper();
ForecastRequest forecastRequest = mapper.readValue(message, ForecastRequest.class);
JobDetail jobDetail = requestForecastService.refreshForecasts(forecastRequest);
if (jobDetail.getJobStatus() != JobStatus.complete) {
LOGGER.error("Failed to Refresh Forecast for ProgramId-{}, JobId-{}, JobStatus-{}",
forecastRequest.getProgramId(), jobDetail.getJobId(), jobDetail.getJobStatus());
throw new Exception("Internal Server Error");
}
} catch (Exception e) {
LOGGER.error("Failed to Refresh Forecast for Forecast Request {}", message, e);
throw e;
}
}
}
Kafka Consumer Test :
#RunWith (SpringRunner.class)
#ActiveProfiles ("kafkatest")
#SpringBootTest (classes = ForecastEventConsumerApplication.class)
#DirtiesContext
public class KafkaEventConsumerTest {
private static String TOPIC = "topic-name";
#Mock
private MyServiceImpl myServiceMock;
#InjectMocks
private KafkaEventConsumer kafkaEventConsumer;
private KafkaTemplate<String, String> template;
#Autowired
private KafkaListenerEndpointRegistry kafkaListenerEndpointRegistry;
#ClassRule
public static final KafkaEmbedded embeddedKafka = new KafkaEmbedded(1, true,3, TOPIC);
#Before
public void setUp() throws Exception {
kafkaEventConsumer = new KafkaEventConsumer(myServiceMock);
// set up the Kafka producer properties
Map<String, Object> senderProperties = KafkaTestUtils.senderProps(embeddedKafka.getBrokersAsString());
// create a Kafka producer factory
ProducerFactory<String, String> producerFactory = new DefaultKafkaProducerFactory<String, String>(senderProperties);
// create a Kafka template
template = new KafkaTemplate<>(producerFactory);
// set the default topic to send to
template.setDefaultTopic(TOPIC);
// wait until the partitions are assigned
for (MessageListenerContainer messageListenerContainer : kafkaListenerEndpointRegistry.getListenerContainers()) {
messageListenerContainer.setupMessageListener(new MessageListener<String, String>() {
#Override
public void onMessage(ConsumerRecord<String, String> record) {
try {
kafkaEventConsumer.receive(record.value());
} catch (Exception e) {
e.printStackTrace();
}
}
});
ContainerTestUtils.waitForAssignment(messageListenerContainer, embeddedKafka.getPartitionsPerTopic());
}
}
#AfterClass
public static void tearDown() throws Exception {
embeddedKafka.destroy();
}
#Test
public void testReceive() throws Exception {
String forecastRequestMessage = "{\"programId\":100011770}";
ForecastRequest forecastRequest = ForecastRequest.builder().programId(100011770L).build();
JobDetail jobDetail = JobDetail.builder().jobStatus(JobStatus.complete).build();
Mockito.when(forecastServiceMock.refreshForecasts(Matchers.any())).thenReturn(jobDetail);
template.sendDefault(forecastRequestMessage);
Thread.sleep(2000L);
// validate something
}
}
The problem is, in the above #Test method instead of calling the mocked version of MyService it is calling the original MyService implementation. Also, while debugging my code I found that overridden onMessage() is also not getting called. Please help me in finding what am I doing wrong here.
You have to stop() all the MessageListenerContainers before calling their setupMessageListener(). Then you will need to start() them back to let them to pick up a fresh listener:
protected void doStart() {
...
Object messageListener = containerProperties.getMessageListener();
Assert.state(messageListener != null, "A MessageListener is required");
Anyway that sounds like you really would like to mock only your MyService which is injected into the real KafkaEventConsumer. So, how about to consider to use that like this:
#MockBean
private MyServiceImpl myServiceMock;
And you won't need to do anything in your #Before and no need in the #InjectMocks.
The KafkaEmbedded can expose its host/port (or brokers) properties to the expected Spring Boot conventional configuration properties like this:
#BeforeClass
public static void setup() {
System.setProperty("spring.kafka.bootstrap-servers", kafkaEmbedded.getBrokersAsString());
}
https://docs.spring.io/spring-boot/docs/2.0.0.RELEASE/reference/htmlsingle/#boot-features-testing-spring-boot-applications-mocking-beans