Spring jmsTemplate send Unit testing doen't work - testing

My service method looks like below, I am trying to mock JmsTemplate so that it can send message during unit testing, but it doesn't execute jmsTemplate.send(...), it directly goes to next line, How can i execute jmsTemplate.send(..) part of code of my service class using unit testing?
public int invokeCallbackListener(final MyObject payload, final MyTask task) throws Exception{
//create map of payload and taskId
int taskStatusCd = task.getTaskSatus().getCode();
final Map<String, Object> map = new HashMap<String, Object>();
map.put(PAYLOAD_KEY, payload);
map.put(TASK_ID_KEY, task.getTaskId());
//generate JMSCorrelationID
final String correlationId = UUID.randomUUID().toString();
String requestQueue = System.getProperty("REQUEST_QUEUE");
requestQueue = requestQueue!=null?requestQueue:ExportConstants.DEFAULT_REQUEST_QUEUE;
jmsTemplate.send(requestQueue, new MessageCreator() {
#Override
public Message createMessage(Session session) throws JMSException {
***ObjectMessage message = session.createObjectMessage((Serializable)map)***; //fail here. Message returns null
message.setJMSCorrelationID(correlationId);
message.setStringProperty(MESSAGE_TYPE_PROPERTY,payload.getMessageType().getMessageType());
return message;
}
});
l.info("Request Message sent with correlationID: " + correlationId);
taskStatusCd = waitForResponseStatus(task.TaskId(), taskStatusCd, correlationId);
return taskStatusCd;
}
This is my test class code.
RemoteInvocationService remoteInvocationService;
JmsTemplate mockTemplate;
Session mockSession;
Queue mockQueue;
ObjectMessage mockMessage;
MessageCreator mockmessageCreator;
#Before
public void setUp() throws Exception {
remoteInvocationService = new RemoteInvocationService();
mockTemplate = mock(JmsTemplate.class);
mockSession = mock(Session.class);
mockQueue = mock(Queue.class);
mockMessage = mock(ObjectMessage.class);
mockmessageCreator = mock(MessageCreator.class);
when(mockSession.createObjectMessage()).thenReturn(mockMessage);
when(mockQueue.toString()).thenReturn("testQueue");
Mockito.doAnswer(new Answer<Message>() {
#Override
public Message answer(final InvocationOnMock invocation) throws JMSException {
final Object[] args = invocation.getArguments();
final String arg2 = (String)args[0];
final MessageCreator arg = (MessageCreator)args[1];
return arg.createMessage(mockSession);
}
}).when(mockTemplate).send(Mockito.any(MessageCreator.class));
mockTemplate.setDefaultDestination(mockQueue);
remoteInvocationService.setJmsTemplate(mockTemplate);
}
#Test
public void testMessage() throws Exception{
MyTask task = new MyTask();
task.setTaskSatus(Status.Pending);
remoteInvocationService.invokeCallbackListener(new MyObject(), task);
}
I have below code which receives message but, I am getting status object null.
Message receivedMsg = jmsTemplate.receiveSelected(responseQueue, messageSelector);if(receivedMsg instanceof TextMessage){
TextMessage status = (TextMessage) receivedMsg;
l.info(status.getText());}
below test code:
TextMessage mockTextMessage;
when(mockSession.createTextMessage()).thenReturn(mockTextMessage);
mockTextMessage.setText("5");
when(mockTemplate.receiveSelected(Mockito.any(String.class), Mockito.any(String.class))).thenReturn(mockTextMessage)

You are mocking the send method that accepts only one parameter (MessageCreator), but you are actually calling the one that accepts two (String, MessageCreator).
Add the String to your mock:
Mockito.doAnswer(new Answer<Message>() {
#Override
public Message answer(final InvocationOnMock invocation) throws JMSException {
final Object[] args = invocation.getArguments();
final MessageCreator arg = (MessageCreator)args[0];
return arg.createMessage(mockSession);
}
}).when(mockTemplate).send(Mockito.any(String.class), Mockito.any(MessageCreator.class));
There is another mistake when mocking the sesssion. You are mocking the method without parameterers:
when(mockSession.createObjectMessage()).thenReturn(mockMessage);
but you actually need to mock the one with the Serializable param:
when(mockSession.createObjectMessage(Mockito.any(Serializable.class)).thenReturn(mockMessage);

Related

Trouble in testing with invalid field

public with sharing class SobjectByParams {
public SObject createSObject(String sObjectName, Map<String, String> fields) {
String invalidSObjectError = System.Label.invalid_Sobject_Name;
String invalidFieldError = System.Label.Invalid_Sobject_Field;
SObject newObject;
try {
newObject = (SObject) Type.forName(sObjectName).newInstance();
} catch (NullPointerException ex) {
throw new InvalidTypeNameException(invalidSObjectError);
}
for (String field : fields.keySet()) {
try {
newObject.put(field, fields.get(field));
} catch (SObjectException ex) {
throw new InvalidTypeNameException(invalidFieldError);
}
}
insert newObject;
return newObject;
}
public class InvalidTypeNameException extends Exception {
}
}
#IsTest
public with sharing class SobjectByParamsTest {
SobjectByParams sobjectByParams;
private static final String TestName = 'TestName';
private static final String BCity = 'Lviv';
private static final String LastName = 'Kapo';
private static final String Email = 'email';
#IsTest
static void createSObject() {
SobjectByParams sobjectByParams = new SobjectByParams();
Map<String, String> fields = new Map<String, String>();
fields.put('BillingCity', BCity);
Test.startTest();
SObject result = sobjectByParams.createSObject(TestName, fields);
Test.stopTest();
System.assertEquals(BCity, result.get(BCity));
}
}
SobjectByParams.InvalidTypeNameException: invalidSobjectNameError - PROBLEM
TEST WORKING on 53.33% but I need min 80%
I don`t know how to fix my problem.
Shouldn't TestName be a sObject name like Contact? Does this code work when you run it normally, does it insert anything or throw? Check/fix that and then you probably need a negative test too.
Make second test method
#isTest
static void testPassingBadData() {
SobjectByParams sobjectByParams = new SobjectByParams();
Map<String, String> fields = new Map<String, String>();
fields.put('BillingCity', BCity);
try{
sobjectByParams.createSObject('NoSuchObjectInTheSystem', fields);
System.assert(false, 'This should have failed and thrown exception');
} catch(Exception e){
System.assert(e.getMessage().contains(Label.Invalid_Sobject_Field));
}
}

How to mock an s3ObjectStream

I have code to test as follows:
#BeforeEach
static void setup() throws IOException {
Context context = new MockLambdaContext();
mockAmazonS3Client();
}
#Test
#DisplayName("Testing handleRequest returns complete when middle base64 piece")
public void handleRequestTestMiddlePiece() throws IOException {
ApplicationHandler applicationHandler = new ApplicationHandler();
mockAmazonS3Client();
input.replace("tag","middle");
assertEquals("complete", applicationHandler.handleRequest(input, context));
}
public static void mockAmazonS3Client() throws IOException {
AmazonS3Client mockAmazonS3Client = mock(AmazonS3Client.class);
S3Object s3Object = mock(S3Object.class);
S3ObjectInputStream s3ObjectInputStream = mock(S3ObjectInputStream.class);
InputStream testInputStream = new ByteArrayInputStream("Test".getBytes());
when(mockAmazonS3Client.getObject(any(String.class), any(String.class))).thenAnswer(invocationOnMock -> {
return s3Object;
});
when(s3ObjectInputStream.read(any(byte[].class))).thenAnswer(invocation -> {
return testInputStream.read(invocation.getArgument(0));
});
when(s3Object.getObjectContent()).thenReturn(s3ObjectInputStream);
new MockUp<AmazonS3Client>() {
#Mock
public S3Object getObject(String bucketName, String key) throws SdkClientException, AmazonServiceException {
return s3Object;
}
};
new MockUp<AmazonS3Client>() {
#Mock
PutObjectResult putObject(PutObjectRequest var1) throws SdkClientException, AmazonServiceException {
return null;
}
};
new MockUp<AmazonS3Client>() {
#Mock
public S3ObjectInputStream getObjectContent() {
return s3ObjectInputStream;
}
};
}
In the mockAmazonS3Client I have mocked the S3Object and the S3ObjectStream that getObject produces using mockito. I have managed to use a 'when' to have getObjectContent call an S3ObjectStream. However, when I use another 'when' to mock what happens if the S3ObjectSteam is read it throws an exception where 0 bytes are returned:
java.io.IOException: Underlying input stream returned zero bytes
It could be the second 'when' that is failing for some reason, though I'm not sure. Any thoughts will be gratefully received. Thanks.

NPE when trying to use Jetty async HTTP client

When trying to use Firebase Cloud Messaging by Google with the help of non-blocking Jetty HTTP client in a simple test case that I have prepared at GitHub -
private static final HttpClient sHttpClient = new HttpClient();
private static final Response.ContentListener sFcmListener = new Response.ContentListener() {
#Override
public void onContent(Response response, ByteBuffer content) {
if (response.getStatus() != 200) {
return;
}
String body = StandardCharsets.UTF_8.decode(content).toString();
System.out.printf("onContent: %s\n", body);
Map<String, Object> resp = (Map<String, Object>) JSON.parse(body);
try {
Object[] results = (Object[]) resp.get(FCM_RESULTS);
Map result = (Map) results[0];
String error = (String) result.get(FCM_ERROR);
if (FCM_NOT_REGISTERED.equals(error)) {
// TODO delete invalid FCM token from the database
}
} catch (Exception ignore) {
}
}
};
public static void main(String[] args) throws Exception {
sHttpClient.start();
sHttpClient.POST(FCM_URL)
.header(HttpHeader.AUTHORIZATION, FCM_KEY)
.header(HttpHeader.CONTENT_TYPE, "application/json")
.content(new StringContentProvider(JSON.toString(REQUEST)))
.onResponseContent(sFcmListener)
.send();
}
but unfortunately the execution fails immediately with NPE:
2017-06-30 10:46:41.312:INFO::main: Logging initialized #168ms to org.eclipse.jetty.util.log.StdErrLog
Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.NullPointerException
at org.eclipse.jetty.client.util.FutureResponseListener.getResult(FutureResponseListener.java:118)
at org.eclipse.jetty.client.util.FutureResponseListener.get(FutureResponseListener.java:101)
at org.eclipse.jetty.client.HttpRequest.send(HttpRequest.java:682)
at de.afarber.fcmnotregistered.Main.main(Main.java:68)
Caused by: java.lang.NullPointerException
at org.eclipse.jetty.io.ssl.SslClientConnectionFactory.newConnection(SslClientConnectionFactory.java:59)
at org.eclipse.jetty.client.AbstractHttpClientTransport$ClientSelectorManager.newConnection(AbstractHttpClientTransport.java:191)
at org.eclipse.jetty.io.ManagedSelector.createEndPoint(ManagedSelector.java:420)
at org.eclipse.jetty.io.ManagedSelector.access$1600(ManagedSelector.java:61)
at org.eclipse.jetty.io.ManagedSelector$CreateEndPoint.run(ManagedSelector.java:599)
at org.eclipse.jetty.util.thread.Invocable.invokePreferred(Invocable.java:128)
at org.eclipse.jetty.util.thread.Invocable$InvocableExecutor.invoke(Invocable.java:222)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:294)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:199)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:672)
at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:590)
at java.lang.Thread.run(Thread.java:745)
Why does it happen please?
UPDATE:
I have switched to using BufferingResponseListener and the NPE is gone, but now the program prints java.net.NoRouteToHostException: No route to host even though the Google FCM endpoint is a well-known host:
private static final HttpClient sHttpClient = new HttpClient();
private static final BufferingResponseListener sFcmListener = new BufferingResponseListener() {
#Override
public void onComplete(Result result) {
if (!result.isSucceeded()) {
System.err.println(result.getFailure()); // No route to host
return;
}
String body = getContentAsString(StandardCharsets.UTF_8);
System.out.printf("onContent: %s\n", body);
Map<String, Object> resp = (Map<String, Object>) JSON.parse(body);
try {
Object[] results = (Object[]) resp.get(FCM_RESULTS);
Map map = (Map) results[0];
String error = (String) map.get(FCM_ERROR);
if (FCM_NOT_REGISTERED.equals(error)) {
// TODO delete invalid FCM token from the database
}
} catch (Exception ex) {
System.err.println(ex);
}
}
};
public static void main(String[] args) throws Exception {
sHttpClient.start();
sHttpClient.POST(FCM_URL)
.header(HttpHeader.AUTHORIZATION, FCM_KEY)
.header(HttpHeader.CONTENT_TYPE, "application/json")
.content(new StringContentProvider(JSON.toString(REQUEST)))
.send(sFcmListener);
}
I get the No route to host for any FCM_URL value I try, why?
Adding SslContextFactory has helped me:
private static final SslContextFactory sFactory = new SslContextFactory();
private static final HttpClient sHttpClient = new HttpClient(sFactory);
private static final BufferingResponseListener sFcmListener = new BufferingResponseListener() {
#Override
public void onComplete(Result result) {
if (!result.isSucceeded()) {
System.err.println(result.getFailure());
return;
}
String body = getContentAsString(StandardCharsets.UTF_8);
System.out.printf("onComplete: %s\n", body);
try {
Map<String, Object> resp = (Map<String, Object>) JSON.parse(body);
Object[] results = (Object[]) resp.get(FCM_RESULTS);
Map map = (Map) results[0];
String error = (String) map.get(FCM_ERROR);
System.out.printf("error: %s\n", error);
if (FCM_NOT_REGISTERED.equals(error) ||
FCM_MISSING_REGISTRATION.equals(error) ||
FCM_INVALID_REGISTRATION.equals(error)) {
// TODO delete invalid FCM token from the database
}
} catch (Exception ex) {
System.err.println(ex);
}
}
};
public static void main(String[] args) throws Exception {
sHttpClient.start();
sHttpClient.POST(FCM_URL)
.header(HttpHeader.AUTHORIZATION, FCM_KEY)
.header(HttpHeader.CONTENT_TYPE, "application/json")
.content(new StringContentProvider(JSON.toString(REQUEST)))
.send(sFcmListener);
}
The still open question I have is how to retrieve the invalid FCM token that I have used in the Jetty HTTP client request, so that I can delete it from my database on the response...

How To update google-cloud-dataflow running in app engine without clearing bigquery tables

I have a google-cloud-dataflow process running on the App-engine.
It listens to messages sent via pubsub and streams to big-query.
I updated my code and I am trying to rerun the app.
But I receive this error:
Exception in thread "main" java.lang.IllegalArgumentException: BigQuery table is not empty
Is there anyway to update data flow without deleting the table?
Since my code might change quite often, and I do not want to delete data in the table.
Here is my code:
public class MyPipline {
private static final Logger LOG = LoggerFactory.getLogger(BotPipline.class);
private static String name;
public static void main(String[] args) {
List<TableFieldSchema> fields = new ArrayList<>();
fields.add(new TableFieldSchema().setName("a").setType("string"));
fields.add(new TableFieldSchema().setName("b").setType("string"));
fields.add(new TableFieldSchema().setName("c").setType("string"));
TableSchema tableSchema = new TableSchema().setFields(fields);
DataflowPipelineOptions options = PipelineOptionsFactory.as(DataflowPipelineOptions.class);
options.setRunner(BlockingDataflowPipelineRunner.class);
options.setProject("my-data-analysis");
options.setStagingLocation("gs://my-bucket/dataflow-jars");
options.setStreaming(true);
Pipeline pipeline = Pipeline.create(options);
PCollection<String> input = pipeline
.apply(PubsubIO.Read.subscription(
"projects/my-data-analysis/subscriptions/myDataflowSub"));
input.apply(ParDo.of(new DoFn<String, Void>() {
#Override
public void processElement(DoFn<String, Void>.ProcessContext c) throws Exception {
LOG.info("json" + c.element());
}
}));
String fileName = UUID.randomUUID().toString().replaceAll("-", "");
input.apply(ParDo.of(new DoFn<String, String>() {
#Override
public void processElement(DoFn<String, String>.ProcessContext c) throws Exception {
JSONObject firstJSONObject = new JSONObject(c.element());
firstJSONObject.put("a", firstJSONObject.get("a").toString()+ "1000");
c.output(firstJSONObject.toString());
}
}).named("update json")).apply(ParDo.of(new DoFn<String, TableRow>() {
#Override
public void processElement(DoFn<String, TableRow>.ProcessContext c) throws Exception {
JSONObject json = new JSONObject(c.element());
TableRow row = new TableRow().set("a", json.get("a")).set("b", json.get("b")).set("c", json.get("c"));
c.output(row);
}
}).named("convert json to table row"))
.apply(BigQueryIO.Write.to("my-data-analysis:mydataset.mytable").withSchema(tableSchema)
);
pipeline.run();
}
}
You need to specify withWriteDisposition on your BigQueryIO.Write - see documentation of the method and of its argument. Depending on your requirements, you need either WRITE_TRUNCATE or WRITE_APPEND.

Mule FunctionalTestComponent event callbacks - event is never received

I am trying to use the FunctionalTestComponent to catch messages in Mule so I can assert against certain Header properties mid flow. But events are never received and my test always passes where it should fail. How can I configure my test to catch the events? Here is my test method:
#Test
public void testCallback() throws Exception {
FunctionalTestComponent ftc = getFunctionalTestComponent("test");
ftc.setEventCallback(new EventCallback()
{
public void eventReceived(MuleEventContext context, Object component)
throws Exception
{
assertTrue(false);
System.out.println("Thanks for calling me back");
}
});
MuleClient client = muleContext.getClient();
MuleMessage reply = client.send("vm://test", TEST_MESSAGE, null, 5000);
}
The config is just 2 flows with a test:component in the second flow which is referenced by the vm://test flow.
The only way I could get it to work was by using a latch and an AtomicReference to save the MuleMessage so I can run assertions after the muleClient.send.
FunctionalTestComponent ftc = getFunctionalTestComponent("test");
final CountDownLatch latch = new CountDownLatch(1);
final AtomicReference<MuleMessage> message = new AtomicReference<MuleMessage>();
EventCallback callback = new EventCallback() {
public void eventReceived(MuleEventContext context, Object component)
throws Exception {
if (1 == latch.getCount()) {
message.set(context.getMessage());
System.out.println("1111");
latch.countDown();
}
}
};
ftc.setEventCallback(callback);
MuleClient client = muleContext.getClient();
client.send("vm://test", TEST_MESSAGE, null);
latch.await(10, TimeUnit.SECONDS);
MuleMessage msg = (MuleMessage) message.get();