How to use JunitTest with Camel, ActiveMQ and external API - spring-boot

I'm trying to set up JunitTest using camel, activeMq and an Alfresco API
The route I want to test is :
from(Constantes.Direct.DIRECT_GET_AUTHENTIFICATION_TICKET)
.setBody().simple("{"
+ "\"userId\": \"userId\","
+"\"password\": \"password\""
+"}")
.setHeader(Exchange.HTTP_METHOD,constant(Constantes.Headers.HTTP_POST))
.setHeader(Exchange.HTTP_URI,simple(Constantes.Urls.OBTENIR_TICKET))
.to(Constantes.Urls.DUMMYHOST).convertBodyTo(String.class)
.unmarshal().json(JsonLibrary.Jackson, TicketAlfresco.class).process(new Dumper())
.process(new TokenBase64Proc())
.setHeader(Constantes.Headers.SENDER, constant(Constantes.Headers.ALFRESCO))
.setHeader(Constantes.Headers.API_ACTION, constant(SET_ALFRESCO_TOKEN))
.setHeader(Constantes.Headers.HEADER_AUTHORIZATION, simple("${body}"))
.inOut(Constantes.ActiveMq.ACTIVEMQ_IN)
.end();
The first "to" send a request to the Alfresco API and give back a new token.
The last inOut send the token to an activeMQ.
The problem is that when I want to test my route, when the test arrive to inOut inside the activeMq, the test fail because it didn't get any answer.
Do I need to install and embeded broker activeMQ or do I need to Mock the ActiveMQ ? And how can I do that?
For the moment to make it run I use :
mockEndpointsAndSkip("activemq:IN")
But I'm not sure that is the good solution.
Here is the test I have for the moment:
#RunWith(SpringRunner.class)
#EnableAutoConfiguration
#ComponentScan(basePackages = {"fr.gif.wsp.web.service.alfresco*"})
public class RouteGetAuthentificationTicketTest extends CamelTestSupport{
#Autowired private RouteGetAuthentificationTicket routeGetAuthentificationTicket;
//Route to test
private final static String FOURNISSEUR_GET_AUTHENTIFICATION_TICKET = Constantes.Direct.DIRECT_GET_AUTHENTIFICATION_TICKET;
private final static String MOCK_FOURNISSEUR_GET_AUTHENTIFICATION_TICKET = "mock:" + FOURNISSEUR_GET_AUTHENTIFICATION_TICKET;
// Mock result
private final static String MOCK_RESULT = "mock:result";
//Data
private final static String BODY = "Content of the body";
#Override
protected RoutesBuilder createRouteBuilder() {
return routeGetAuthentificationTicket;
}
#Before
public void setContextRoute() throws Exception {
context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
mockEndpointsAndSkip("activemq:IN");
weaveAddLast().to(MOCK_RESULT);
}
});
}
#Test
public void getAuthentificationTicket() throws InterruptedException {
final MockEndpoint resultEndpoint = context.getEndpoint(MOCK_FOURNISSEUR_GET_AUTHENTIFICATION_TICKET, MockEndpoint.class);
context.createProducerTemplate().sendBody(FOURNISSEUR_GET_AUTHENTIFICATION_TICKET, BODY);
resultEndpoint.assertIsSatisfied();
final Object result = context.createProducerTemplate().requestBody(FOURNISSEUR_GET_AUTHENTIFICATION_TICKET, BODY);
assertNotNull(result);
}}
Thanks for your time

Related

Error when mocking a repository in unit test of a spring webflux application (using JUnit and Mockito)?

I am yet to find a solution to this.
My Test class:
#WebFluxTest(controllers = {PatronController.class})
#Import({PatronService.class}) //to satisfy PatronController dependency.
#ExtendWith({PatronParameterResolver.class})
class PatronFunctionsSpec {
private Patron patron;
private Mono<Patron> patronMono;
private final PatronService patronService = Mockito.mock(PatronService.class);
#MockBean
private PatronRepository patronRepository;
#Autowired
private WebTestClient client;
#BeforeEach
void init(Patron injectedPatron) {
patron = injectedPatron;
patronMono = Mono.just(patron);
}
//Patron Story: patron wants to create an account with us
#Nested
#DisplayName("Creating a patron.")
class CreatingPatron {
#Test
#DisplayName("PatronService.create() returns success msg in Response obj after creating patron.")
void getResponseObjFromServiceCreate() {
Flux<Patron> patronFlux = Flux.from(patronMono);
Mockito.when(patronRepository.saveAll(patronMono)).thenReturn(patronFlux);
PatronService patronService = new PatronService(patronRepository);
Mono<Response> responseStream = patronService.create(Mono.just(patron));
Mono<Response> expectedResponseStream = Mono.just(new Response("Welcome, patron. Can't show emojis yet -- sorry."));
assertEquals(expectedResponseStream, responseStream);
}
}
}
See my PatronService class with its code:
#Service
public class PatronService {
private final PatronRepository patronRepository;
public PatronService(PatronRepository patronRepository) {
this.patronRepository = patronRepository;
}
/**
*
* persists patron via PatronRepo
*/
public Mono<Response> create(Mono<Patron> patronMono) {
patronRepository.saveAll(patronMono).subscribe();
return Mono.just(new Response("Welcome, patron. Can't find the emojis yet -- sorry."));
}
}
I am testing the PatronService's create(), so need to mock and stub PatronRepository and its function respectively. But the problem is: after running the test case, I get this exception:
java.lang.NullPointerException: Cannot invoke "reactor.core.publisher.Flux.subscribe()" because "patronFlux" is null
at com.budgit.service.PatronService.create(PatronService.java:26)
How can I fix this?

Tests fail with #Scheduled Task: JdbcSQLSyntaxErrorException Table "USER_ACCOUNT_CREATED_EVENT" not found

Summary & first problem
I am trying to test my user registration mechanism. When a new user account is created via my REST API, a UserAccountCreatedEvent is stored in the database. A scheduled task checks the database every 5 seconds for new UserAccountCreatedEvents and if one is present, sends an email to the registered user. When running my tests I encounter the problem that the table for the UserAccountCreatedEvent can't be found (see exception below). I used to send the email in a blocking manner in the service method, but I recently switched to this async approach. All my tests worked perfectly for the blocking approach and the only thing I changed for the async approach is to include Awaitility in the test.
2019-04-23 11:24:51.605 ERROR 7968 --- [taskScheduler-1] o.s.s.s.TaskUtils$LoggingErrorHandler : Unexpected error occurred in scheduled task.
org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [select useraccoun0_.id as id1_0_, useraccoun0_.completed_at as complete2_0_, useraccoun0_.created_at as created_3_0_, useraccoun0_.in_process_since as in_proce4_0_, useraccoun0_.status as status5_0_, useraccoun0_.user_id as user_id1_35_ from user_account_created_event useraccoun0_ where useraccoun0_.status=? order by useraccoun0_.created_at asc limit ?]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement
Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException:
Table "USER_ACCOUNT_CREATED_EVENT" not found; SQL statement:
select useraccoun0_.id as id1_0_, useraccoun0_.completed_at as complete2_0_, useraccoun0_.created_at as created_3_0_, useraccoun0_.in_process_since as in_proce4_0_, useraccoun0_.status as status5_0_, useraccoun0_.user_id as user_id1_35_ from user_account_created_event useraccoun0_ where useraccoun0_.status=? order by useraccoun0_.created_at asc limit ? [42102-199]
Full stack trace
Second problem
As if that were not enough, the tests behave completely different when running them in debug mode. When I set a breakpoint in the method that is called by the method which is annotated with #Scheduled, it is invoked several times althogh #Scheduled is configured with a fixedDelayString (fixed delay) of 5000ms. Thanks to logging I can even see that several mails were sent. Still, my test SMTP sever (GreenMail) does not receive any emails. How is this even possible? I've intentionally set the transaction isolation to Isolation.SERIALIZABLE so that it should be impossible (as far as I understand transaction isolation) that two scheduled methods access the same Event from the database.
Third problem
To cap it all, when I rerun the failed tests, THEY WORK. But, there are different exceptions on the console (see below). But still, the app starts and the tests finish successfully. There are different test results depending on if I run all tests vs. only the class vs. only the method vs. rerun failed tests. I don't understand how such an indeterministic behaviour can be possible.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: Failed to scan classpath for unlisted entity classes
Caused by: java.nio.channels.ClosedByInterruptException: null
Full stack trace
My code
Test class (UserRegistrationTest)
#ActiveProfiles("test")
#AutoConfigureMockMvc
#RunWith(SpringRunner.class)
#SpringBootTest
#DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
public class UserRegistrationTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private ObjectMapper objectMapper;
#Autowired
private Routes routes;
#Autowired
private TestConfig testConfig;
#Resource(name = "validCustomerDTO")
private CustomerDTO validCustomerDTO;
#Resource(name = "validVendorDTO")
private VendorRegistrationDTO validVendorRegistrationDTO;
#Value("${schedule.sendRegistrationConfirmationEmailTaskDelay}")
private Short registrationConfirmationEmailSenderTaskDelay;
private GreenMail smtpServer;
// Setup & tear down
#Before
public void setUp() {
smtpServer = testConfig.getMailServer();
smtpServer.start();
}
#After
public void tearDown() {
smtpServer.stop();
}
// Tests
#Test
public void testCreateCustomerAccount() throws Exception {
mockMvc.perform(
post(routes.getCustomerPath())
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(objectMapper.writeValueAsString(validCustomerDTO)))
.andExpect(status().isCreated());
// When run normally, I get a timeout from the next line
await().atMost(registrationConfirmationEmailSenderTaskDelay + 10000, MILLISECONDS).until(smtpServerReceivedOneEmail());
// Verify correct registration confirmation email was sent
MimeMessage[] receivedMessages = smtpServer.getReceivedMessages();
assertThat(receivedMessages).hasSize(1);
// other checks
// ...
}
#Test
public void testCreateVendorAccount() throws Exception {
mockMvc.perform(
post(routes.getVendorPath())
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(objectMapper.writeValueAsString(validVendorRegistrationDTO)))
.andExpect(status().isCreated());
// When run normally, I get a timeout from the next line
await().atMost(registrationConfirmationEmailSenderTaskDelay + 10000, MILLISECONDS).until(smtpServerReceivedOneEmail());
// Verify correct registration confirmation email was sent
MimeMessage[] receivedMessages = smtpServer.getReceivedMessages();
assertThat(receivedMessages).hasSize(1);
// other checks
// ...
}
// Helper methods
private Callable<Boolean> smtpServerReceivedOneEmail() {
return () -> smtpServer.getReceivedMessages().length == 1;
}
// Test configuration
#TestConfiguration
static class TestConfig {
private static final int PORT = 3025;
private static final String HOST = "localhost";
private static final String PROTOCOL = "smtp";
GreenMail getMailServer() {
return new GreenMail(new ServerSetup(PORT, HOST, PROTOCOL));
}
#Bean
public JavaMailSender javaMailSender() {
JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
javaMailSender.setHost(HOST);
javaMailSender.setPort(PORT);
javaMailSender.setProtocol(PROTOCOL);
javaMailSender.setDefaultEncoding("UTF-8");
return javaMailSender;
}
}
Task scheduler (BusinessTaskScheduler)
#Component
public class BusinessTaskScheduler {
private final RegistrationTask registrationTask;
#Autowired
public BusinessTaskScheduler(RegistrationTask registrationTask) {
this.registrationTask = registrationTask;
}
#Scheduled(fixedDelayString = "${schedule.sendRegistrationConfirmationEmailTaskDelay}")
public void sendRegistrationConfirmationEmail() {
registrationTask.sendRegistrationConfirmationEmail();
}
}
The code that is called by the scheduled method (RegistrationTask)
#Component
#Transactional(isolation = Isolation.SERIALIZABLE)
public class RegistrationTask {
private final EmailHelper emailHelper;
private final EventService eventService;
private final UserRegistrationService userRegistrationService;
#Autowired
public RegistrationTask(EmailHelper emailHelper, EventService eventService, UserRegistrationService userRegistrationService) {
this.emailHelper = emailHelper;
this.eventService = eventService;
this.userRegistrationService = userRegistrationService;
}
public void sendRegistrationConfirmationEmail() {
Optional<UserAccountCreatedEvent> optionalEvent = eventService.getOldestUncompletedUserAccountCreatedEvent();
if (optionalEvent.isPresent()) {
UserAccountCreatedEvent event = optionalEvent.get();
User user = event.getUser();
RegistrationVerificationToken token = userRegistrationService.createRegistrationVerificationTokenForUser(user);
emailHelper.sendRegistrationConfirmationEmail(token);
eventService.completeEvent(event);
}
}
}
The event service (EventServiceImpl)
#Service
#Transactional(isolation = Isolation.SERIALIZABLE)
public class EventServiceImpl implements EventService {
private final ApplicationEventDAO applicationEventDAO;
private final UserAccountCreatedEventDAO userAccountCreatedEventDAO;
#Autowired
public EventServiceImpl(ApplicationEventDAO applicationEventDAO, UserAccountCreatedEventDAO userAccountCreatedEventDAO) {
this.applicationEventDAO = applicationEventDAO;
this.userAccountCreatedEventDAO = userAccountCreatedEventDAO;
}
#Override
public void completeEvent(ApplicationEvent event) {
if (!event.getStatus().equals(COMPLETED) && Objects.isNull(event.getCompletedAt())) {
event.setStatus(COMPLETED);
event.setCompletedAt(LocalDateTime.now());
applicationEventDAO.save(event);
}
}
#Override
public Optional<UserAccountCreatedEvent> getOldestUncompletedUserAccountCreatedEvent() {
Optional<UserAccountCreatedEvent> optionalEvent = userAccountCreatedEventDAO.findFirstByStatusOrderByCreatedAtAsc(NEW);
if (optionalEvent.isPresent()) {
UserAccountCreatedEvent event = optionalEvent.get();
setEventInProcess(event);
return Optional.of(userAccountCreatedEventDAO.save(event));
}
return Optional.empty();
}
#Override
public void publishEvent(ApplicationEvent event) {
applicationEventDAO.save(event);
}
// Helper methods
private void setEventInProcess(ApplicationEvent event) {
event.setStatus(Status.IN_PROCESS);
event.setInProcessSince(LocalDateTime.now());
}
}
The UserAccountCreatedEvent
application.yml
schedule:
sendRegistrationConfirmationEmailTaskDelay: 5000 # delay between tasks in milliseconds
I am new to scheduling with Spring, so any help is greatly appreciated!

Send websocket message to user across dynos

I have a spring boot application running on heroku. I make use of websockets for sending messages to and from client and server for a specific user . I use spring boot's SimpMessagingTemplate.convertAndSendToUser to send and receive messages, which works fine for when a user needs get a message back from the server. I use Heroku session affinity which means that even if I scale up the number of sessions the user and websocket still share the same session.
My problem comes when I need a user to send a message to another user. It works fine if both users are sharing the session, but not if the message will not come through.
Is it possible to send a message from one user to another across different sessions using, SimpMessagingTemple? Or would I need to use a message broker, eg Redis.
I was looking into implementing sending a message using StringRedisTemplate but not sure how to send a message to a particular user.
private SimpMessagingTemplate messagingTemplate;
#Autowired
public MessageController(SimpMessagingTemplate messagingTemplate) {
this.messagingTemplate = messagingTemplate;
}
#MessageMapping("/secured/user-in")
public void sendToDevice(Message msg, #AuthenticationPrincipal User principal) throws Exception {
if (msg.getTo() != null) {
String email = msg.getTo();
Message out = new Message();
out.setMsg(msg.getMsg());
out.setFrom(msg.getFrom());
out.setTo(msg.getTo());
out.setSentTime(new Date());
out.setStatus(msg.getStatus());
messagingTemplate.convertAndSendToUser(email, "/secured/topic", out);
}
}
JS
function connect() {
var socket = new SockJS('/secured/user-in');
ST.stompClient = Stomp.over(socket);
var headers = {};
headers[ST.getHeader()] = ST.getToken();
ST.getStompClient().connect(headers, function (frame) {
retries = 1;
console.log('Connected: ' + frame);
ST.getStompClient().subscribe('/user/secured/topic', function (event){
var msg = JSON.parse(event.body);
showMessage(msg.msg);
});
});
}
UPDATE 1
I am guessing I could do something like this, as done here:
SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor
.create(SimpMessageType.MESSAGE);
headerAccessor.setSessionId(sessionId);
headerAccessor.setLeaveMutable(true);
messagingTemplate.convertAndSendToUser(sessionId,"/queue/something", payload,
headerAccessor.getMessageHeaders());
But how could I get the session id of another user, I am using Redis to store session info: #EnableRedisHttpSession
I had my terminology a bit mixed up I was trying to send a message to another user on another dyno rather than session.
Ended up using redis sub/pub.
So when a message is receive by the controller it is published to redis, and the redis MessageListenerAdapter envokes the convertAndSendToUser method.
#MessageMapping("/secured/user-in")
public void sendToDevice(Message msg, #AuthenticationPrincipal User principal) throws Exception {
publishMessageToRedis(msg);
}
private void publishMessageToRedis(Message message) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
String messageString = objectMapper.writeValueAsString(message);
stringRedisTemplate.convertAndSend("message", messageString);
}
redis config
#Bean
RedisMessageListenerContainer container( MessageListenerAdapter chatMessageListenerAdapter) throws URISyntaxException {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory());
container.addMessageListener(chatMessageListenerAdapter, new PatternTopic("message"));
return container;
}
#Bean("chatMessageListenerAdapter")
MessageListenerAdapter chatMessageListenerAdapter(RedisReceiver redisReceiver) {
return new MessageListenerAdapter(redisReceiver, "receiveChatMessage");
}
public class RedisReceiver {
private static final Logger LOG = LogManager.getLogger(RedisReceiver.class);
private final WebSocketMessageService webSocketMessageService;
#Autowired
public RedisReceiver(WebSocketMessageService webSocketMessageService) {
this.webSocketMessageService = webSocketMessageService;
}
// Invoked when message is publish to "chat" channel
public void receiveChatMessage(String messageStr) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
Message message = objectMapper.readValue(messageStr, Message.class);
webSocketMessageService.sendChatMessage(message);
}
}
#Service
public class WebSocketMessageService {
private final SimpMessagingTemplate template;
private static final Logger LOG = LogManager.getLogger(WebSocketMessageService.class);
public WebSocketMessageService(SimpMessagingTemplate template) {
this.template = template;
}
public void sendChatMessage(Message message) {
template.convertAndSendToUser(message.getTo(), "/secured/topic", message);
}
}
Solution was based off this git repository

Testing a REST endpoint with Spring, MongoDB using ObjectIds

I'm new to MongoDB and I'm writing a series of unit tests for a Mongo-backed REST web-service. Here's a simple test for a /clients/{id} enpoint :
#RunWith(MockitoJUnitRunner.class)
public class ClientsControllerMockMvcStandaloneTest {
private MockMvc mvc;
#Mock
private ClientsRepository clientsRepository;
#Mock
private ModelMapper modelMapper;
#InjectMocks
private ClientsController clientsController;
private ExceptionHandlerExceptionResolver createExceptionResolver() {
ExceptionHandlerExceptionResolver exceptionResolver = new ExceptionHandlerExceptionResolver() {
#SuppressWarnings("ConstantConditions")
#Override
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(final HandlerMethod handlerMethod,
final Exception exception) {
final Method method = new ExceptionHandlerMethodResolver(RestResponseEntityExceptionHandler.class)
.resolveMethod(exception);
final RestResponseEntityExceptionHandler handler = new RestResponseEntityExceptionHandler();
return new ServletInvocableHandlerMethod(handler, method);
}
};
exceptionResolver.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
exceptionResolver.afterPropertiesSet();
return exceptionResolver;
}
#Before
public void setup() {
JacksonTester.initFields(this, new ObjectMapper());
mvc = MockMvcBuilders.standaloneSetup(clientsController)
.setHandlerExceptionResolvers(createExceptionResolver())
.build();
}
// GET /api/clients/{id} 200
#Test
public void findById_ClientEntryFound_ShouldReturnFoundClientEntry() throws Exception {
final ObjectId id = new ObjectId();
final Client client = Client.builder()
.id(id)
.name("Microsoft")
.build();
final ClientDTO clientDTO = ClientDTO.builder()
.id(id)
.name("Microsoft")
.build();
when(clientsRepository.findById(id))
.thenReturn(Optional.of(client));
when(modelMapper.map(client, ClientDTO.class))
.thenReturn(clientDTO);
mvc.perform(get("/clients/" + id.toString())
.accept(TestUtils.APPLICATION_JSON_UTF8))
.andExpect(content().contentType(TestUtils.APPLICATION_JSON_UTF8))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id", is(id)))
.andExpect(jsonPath("$.name", is("Microsoft")))
.andDo(MockMvcResultHandlers.print());
verify(modelMapper, times(1)).map(client, ClientDTO.class);
verify(clientsRepository, times(1)).findById(id);
verifyNoMoreInteractions(clientsRepository);
}
}
I expect this to work but I'm getting the following :
java.lang.AssertionError: JSON path "$.id"
Expected: is <5c9b9a0289d2b311b150b92c>
but: was <{timestamp=1553701378, machineIdentifier=9032371, processIdentifier=4529, counter=5290284, timeSecond=1553701378, time=1553701378000, date=1553701378000}>
Expected :is <5c9b9a0289d2b311b150b92c>
Actual :<{timestamp=1553701378, machineIdentifier=9032371, processIdentifier=4529, counter=5290284, timeSecond=1553701378, time=1553701378000, date=1553701378000}>
<Click to see difference>
Any help would be appreciated (including any pointers if you think my general approach could be improved!).
Cheers!
Jackson doesn't know your ObjectId instance should be serialized as 5c9b9a0289d2b311b150b92c and not as:
{
"timestamp": 1553701378,
"machineIdentifier": 9032371,
"processIdentifier": 4529,
"counter": 5290284,
"time": 1553701378000,
"date": 1553701378000,
"timeSecond": 1553701378
}
Luckily it's easy to fix. The ObjectId#toString() method (which will internally invoke ObjectId#toHexString()) allows you to convert the ObjectId instance into a 24-byte hexadecimal string representation.
So you could use #JsonSerialize along with ToStringSerializer to have the ObjectId instance represented as a string:
#JsonSerialize(using = ToStringSerializer.class)
private ObjectId id;
Then, in your test, use the ObjectId#toString() method (or ObjectId#toHexString()) for the assertion:
.andExpect(jsonPath("$.id", is(id.toString())))
Alternatively, assuming that you are using Spring Data for MongoDB, instead of ObjectId, you could use:
#Id
private String id;
You also could handle the conversion of ObjectId to String in your mapper layer.

Writing unit tests for camel routes in a SpringBoot application - getting messageCount 0

I am trying to write unit tests for a camel route - its for importing and processing a file
from(fullImportFTP)
.routeId(STUB_FILE_DOWNLOAD_ROUTE_ID)
.onException(Exception.class)
.handled(false)
.log(LoggingLevel.ERROR, STUB_IMPORT_ERROR_CODE)
.end()
.log("Processing Stub file:[${header.CamelFileName}]")
.to(ROUTE_TO_MACE);
from(ROUTE_TO_MACE)
.routeId(STUB_FILE_IMPORT_ROUTE_ID)
.onException(Exception.class)
.handled(false)
.log(LoggingLevel.ERROR, STUB_IMPORT_ERROR_CODE)
.end()
.onException(IOException.class)
.maximumRedeliveries(routesConfig.camelMaximumRetries).redeliveryDelay(routesConfig.camelRedeliveryDelay)
.handled(false)
.log(LoggingLevel.ERROR, STUB_IMPORT_ERROR_CODE)
.end()
.split().tokenizeXML(ITEM).streaming()
.process(processor)
.to("log:route.StubRoute?level=DEBUG")
.end()
.log("Stub file sucessfully processed:[${header.CamelFileName}]");
And below is the unit test:
#RunWith(CamelSpringBootRunner.class)
#SpringBootTest
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class CamelRouteTest {
#EndpointInject(uri = "mock:success_result")
private MockEndpoint successResultEndpoint;
#EndpointInject(uri = "direct:mock-import-stub-download")
private FluentProducerTemplate producer;
#Autowired
private CamelContext camelContext;
#MockBean
RestTemplate restTemplate;
private static final String MOCK_IMPORT_STUB_DOWNLOAD = "direct:mock-import-stub-download";
private static final String TEST_STUB_FILE_LOCATION = "src/test/resources";
#Before
public void setup() throws Exception {
camelContext.getRouteDefinition(STUB_FILE_DOWNLOAD_ROUTE_ID).autoStartup(true).adviceWith(camelContext,
new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
replaceFromWith(MOCK_IMPORT_STUB_DOWNLOAD);
interceptSendToEndpoint("log:route.StubRoute?level=DEBUG").skipSendToOriginalEndpoint().to(successResultEndpoint);
}
});
camelContext.start();
}
#Test
public void testFileDownloadRouter() throws Exception {
File file = new File(TEST_STUB_FILE_LOCATION + "/Stub_11092018_162149_59642501.xml");
successResultEndpoint.expectedMessageCount(1);
producer.withBody(file).withHeader(Exchange.FILE_NAME, "Stub_24102018_162149_59642501.xml").send();
successResultEndpoint.assertIsSatisfied();
}
I always get the message count as 0. Here is the ERROR
java.lang.AssertionError: mock://success_result Received message
count. Expected: <1> but was: <0> Expected :<1> Actual :<0>
What am I doing wrong here? I have 2 routes as you can see - the first one actually goes to the second one, so in the unit tests should I have 2 routes too? I haven't added 2 routes because if I debug I can see that it actually goes through the processor and returning the correct result.
First of all: you are using AdviceWith, so you should put the annotation #UseAdviceWith on your testclass. Otherwise the automatic start of the Camel context and the route advice could overlap.
For the missing message on the Mock: Perhaps your test just asserts too early. I guess the producer does not block while the message is processed, but the MockEndpoint assert follows immediately after sending the message. Right after sending the message the received message count is still 0.
To check if waiting helps, you could insert a Thread.sleep(). If it works, you should get rid of the Thread.sleep() and replace it with a Camel NotifyBuilder
Just saw another point. The final to() in your interceptSendToEndpoint chain points to the MockEndpoint instance variable. I think this should point to the MockEndpoint URI, i.e. .to("mock:success_result")
And even one more: you get the first route to advice with getRouteDefinition(STUB_FILE_DOWNLOAD_ROUTE_ID) but in this advice block you advice both routes. That is probably the reason for your problem. The second route is not adviced and therefore your mock is not in place. You have to advice the second route in its own advice block.
#Before
public void setup() throws Exception {
camelContext.getRouteDefinition(STUB_FILE_DOWNLOAD_ROUTE_ID).autoStartup(true).adviceWith(camelContext, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
replaceFromWith(MOCK_IMPORT_STUB_DOWNLOAD);
}
});
camelContext.getRouteDefinition(STUB_FILE_IMPORT_ROUTE_ID).autoStartup(true).adviceWith(camelContext, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
interceptSendToEndpoint("log:route.StubRoute?level=DEBUG").skipSendToOriginalEndpoint().to("mock:success_result");
}
});
camelContext.start();
}

Resources