Spring cloud stream output destination to object - spring-boot

I am testing a spring cloud stream channel like this:
#RunWith(SpringRunner.class)
#IntegrationTest
#SpringBootTest
#AutoConfigureStubRunner(
stubsMode = StubRunnerProperties.StubsMode.LOCAL,
ids = "br.com.marco.cadeacerva:users:+:stubs:8090")
public class UsersConsumerTest {
#Autowired
StubTrigger trigger;
#MockBean
UserInterestsAggregator consumer;
#Autowired
OutputDestination output;
#Test
public void shouldConsumeUsersEvents() {
trigger.trigger("sendUserMessage");
Message<byte[]> message = output.receive();
verify(consumer).aggregate(any(UserDTO.class));
}
}
I am triggering the message via spring cloud contract machinery, and would like to know if there is a way to receive the message payload of a specific type T instead of a byte[]?
If not, what is the best way to convert the bytes to the original type?

Related

Actually, there were zero interactions with this mock. Embedded Kafka Spring test

I'm trying to see if a method from Service class is invoked when the consumer consumes a message from Kafka topic but i'm getting the error that there is no zero interactions with the Mock. When the test is running it consumes the message and I can see on terminal that the service method is actually invoked (i tried with prints), but it is not passing the test.
My Consumer class:
#Component
public class Consumer {
#Autowired
private Service service;
#KafkaListener(topics = "topic")
public void consume(String message) {
service.add();
}
}
The test:
#SpringBootTest
#RunWith(MockitoJUnitRunner.class)
#DirtiesContext
#EmbeddedKafka(partitions = 1, brokerProperties = { "listeners=PLAINTEXT://localhost:9092", "port=9092" })
class ConsumerTest {
#Mock( lenient = true)
private Service service;
#Autowired
private KafkaTemplate<String, String> kafkaTemplate;
#InjectMocks
private Consumer consumer;
#Test
public void givenEmbeddedKafkaBroker_whenExistsTemperatureMessageInTopic_thenMessageReceivedByConsumerAndServiceInvoked()
throws Exception {
String message = "Hello";
kafkaTemplate.send("topic", message);
Mockito.verify(service, times(1)).add();
}
}
TLDR: Don't use #Mock with #SpringBootTest. Use #MockBean instead.
The components you created in your test don't take part in the message processing:
consumer
service
This stems from the fact that you used #SpringBootTest annotation, which brings up entire application context. This means that Spring creates all services itself, and happily ignores the ones created in the test.
To replace a bean from the test, use #MockBean
To inject a bean created by Spring to your test, use #Autowired

Spring Cloud Stream Supplier function model

I am trying to use spring cloud stream and the new functions support for configuration but I am having a problem to understand how to achieve the same result I would have with annotation configuration.
I need to send a message to the brooker, every time a user is created. With the annotation based configuration I could accomplish it like this:
public UserProducer {
#Autowired
private final Source source;
#Autowired
private final UserRepository repository;
public void saveUser(User user) {
repository.save(user);
source.output().send(MessageBuilder.withPayload(user).build());
}
}
Is it possible to use spring cloud stream functions to achieve the same result?
You can use the new StreamBridge API for doing that. See the docs here: https://cloud.spring.io/spring-cloud-static/spring-cloud-stream/3.0.6.RELEASE/reference/html/spring-cloud-stream.html#_using_streambridge

push a dynamic message in Scheduler websocket using spring boot using stomp

I try to make a chatbot using springboot (websocket), i want to know if it's possible to push a dynamic message in Scheduler, and i need some help, i'can't fugure it out.
I want to push the message in the Scheduler Configure how could i do that:
#EnableScheduling
#Configuration
public class SchedulerConfig {
#Autowired
SimpMessagingTemplate template;
#Scheduled(fixedDelay = 3000)
public void sendAdhocMessages() {
template.convertAndSend("/topic/user", new UserResponse("Fixed Delay Scheduler"));
}
}
in the sendAdhocMessages method i want to pass a message that will be displayed in an html page. in the Official doc it's impossible to pass a parameter to a method which is annotated by #Scheduled, is there any methd to do that?
The official documentation contains a hint to, how you could pass values to the scheduled method. Maybe you could provide a bean that acts as a message provider. In the scheduler class you autowire the message provider and request the messages.
A short code example:
#Componet
public class MessageProvider {
private String message;
// getter and setter ...
}
In the scheduler you could use the message provider like following:
#EnableScheduling
#Configuration
public class SchedulerConfig {
#Autowired
SimpMessagingTemplate template;
#Autowired
MessageProvider messageProvider;
#Scheduled(fixedDelay = 3000)
public void sendAdhocMessages() {
String currentMessage = messageProvider.getMessage();
template.convertAndSend("/topic/user", new UserResponse(currentMessage));
}
}

Cannot inject #Service in Unit Test in SpringBoot project

i have a #Service that I am trying to mock in an Unit Test but i get a null value so far. In the application class I specify what are the scanBasePackages. Do I have to do this in a different way? Thanks.
This is my service class that implements an interface:
#Service
public class DeviceService implements DeviceServiceDao {
private List<Device> devices;
#Override
public List<Device> getDevices(long homeId) {
return devices;
}
}
This is my unit test.
public class SmartHomeControllerTest {
private RestTemplate restTemplate = new RestTemplate();
private static final String BASE_URL = “..”;
#Mock
private DeviceService deviceService;
#Test
public void getHomeRegisteredDevices() throws Exception {
Device activeDevice = new DeviceBuilder()
.getActiveDevice(true)
.getName("Alexa")
.getDeviceId(1)
.getHomeId(1)
.build();
Device inativeDevice = new DeviceBuilder()
.getInactiveDevice(false)
.getName("Heater")
.getDeviceId(2)
.getHomeId(1)
.build();
UriComponentsBuilder builder = UriComponentsBuilder
.fromUriString(BASE_URL + "/1/devices");
List response = restTemplate.getForObject(builder.toUriString(), List.class);
verify(deviceService, times(1)).getDevices(1);
verifyNoMoreInteractions(deviceService);
}
You have to use a Spring test runner if you want to load and use a Spring context during tests execution.
You don't specify any runner, so it uses by default the runner of your test API. Here is probably JUnit or TestNG (the runner using depends on the #Test annotation specified).
Besides, according to the logic of your test, you want to invoke the "real"
REST service :
List response = restTemplate.getForObject(builder.toUriString(),
List.class);
To achieve it, you should load the Spring context and load the Spring Boot container by annotating the test with #SpringBootTest.
If you use a Spring Boot context, to mock the dependency in the Spring context, you must not use #Mock from Mockito but #MockBean from Spring Boot.
To understand the difference between the two, you may refer to this question.
Note that if you are using the #SpringBootTest annotation, a TestRestTemplate is automatically available and can be autowired into your test.
But beware, this is fault tolerant. It may be suitable or not according to your tests.
So your code could look like :
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class SmartHomeControllerTest {
private static final String BASE_URL = “..”;
#Autowired
private TestRestTemplate restTemplate;
#MockBean
private DeviceService deviceService;
#Test
public void getHomeRegisteredDevices() throws Exception {
...
}
As a side note, avoid using raw type as List but favor generic type.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = NotificationApplication.class)
public class EmailClientImplTest {
...
}
And also add the needed properties/configs in
/src/test/resources/application.yml
Good luck!
I figured it out, I am using Mockito and used that to annotate my test class. This allowed me to get a mock of the service class that i am trying to use.
#RunWith(MockitoJUnitRunner.class)
public class SmartHomeControllerTest {..
#Mock
private DeviceService deviceService;
}
Try with #InjectMock instead of #Mock
You should run your test with spring boot runner

How to do integration test with activemq and spring3?

I have a code like this, but I'm not sure how would I test this piece of of code that I extracted from my project. I'm using Spring3 and ActiveMQ. And I'm using spring to do remote HTTPInvoker that's why I have the GateWay. So, when I call method submit in my Gateway, it's going to send a JMS message via JMSDispatcher. How would you inject JmsTemplate to Gateway? As far as I know, if I want to test JMS I have to configure it in application-context.xml in Spring and inject overridden JmsTemplate. So, I could test the message inside the queue? But I can't inject JmsTemplate to Gateway since Mockito will complain about not having that field inside Gateway.
public class Gateway {
#Autowired
private ProcessController processController;
public void submit() {
processControllerFactory.submit();
}
}
public ProcessController {
#Autowired
private JMSDispatcher jmsDispatcher;
public void submit() {
// do something
jmsDispatcher.send(message);
}
}
public JMSDispatcher {
#Autowired
#Qualifier("someJmsTemplate")
private JmsTemplate jmsTemplate;
public void send(MessageCreator message) {
jmsTemplate.send(message);
}
}

Resources