Reputation: 2987
I'm using GCM (Google Cloud Messaging) in order to send notifications to an Android App. My server is using the Google provided gcm-server.jar
and I'm following the documentation. I am able to send notifications without issue to a device.
Now I am trying to unit test business logic based on the Result
(source) returned from PushNotificationDaoGcmImpl
's private method sendNotificationToServer
.
I know that Result
cannot be Mocked due to it being a final
class and that simply instantiating it as new Result()
will not work because there are no public constructors. The Result
inner Builder
class is inaccessible outside of the package com.google.android.gcm.server;
so I can not build the object that way.
I do not find a good way to create a Result
which is what is being returned from the Sender
(source).
My question is how would I go about unit testing that PushNotificationDaoGcmImpl
handles certain conditions based on Result
?
public class PushNotificationDaoGcmImpl{
//method I'm trying to test
public void sendPushNotification(){
// builds message to send
Result result = this.sendNotificationToServer(gcmMessage, deviceToken)
//handle result's error conditions
}
//method call I'm trying to mock
private Result sendNotificationToServer(Message gcmMessage, String deviceToken){
return gcmSender.send(gcmMessage, deviceToken, 1);
}
}
//test snipet
@Test
public void testSendNotificationToServer () throws Exception {
Result result = new Result();
PushNotificationMessage message = new PushNotificationMessage("123", "Test", "deviceToken", "Android");
//Having issue with how to handle Result here
doReturn(result).when(gcmPushNotificationDaoSpy).sendNotificationToServer(any(Message.class), anyString());
PushNotificationResult result = gcmPushNotificationDaoSpy.sendPushNotification(message);
//verify business logic was correct based on Result
}
Upvotes: 3
Views: 1051
Reputation: 1365
You can also create a MockResult class in the com.google.android.gcm.server package which will give you access to the Builder.
package com.google.android.gcm.server;
class MockResult {
public static Result mockResult(...) {
// Use Builder here to construct Result
}
}
To me, this feels easier to manage than dealing with reflection.
Upvotes: 2
Reputation: 2987
The solution I came to was to use reflection to create a Result
object. I'm not sure if this is a great way to do it, but I'm able to test business logic based on different Result
error codes.
@Test
public void testSendNotificationToServer () throws Exception {
String successIndicator = null;
Result result = buildFauxResult("messageId", "deviceToken", successIndicator);
PushNotificationMessage message = new PushNotificationMessage("123", "Test", "deviceToken", "Android");
//Having issue with how to handle Result here
doReturn(result).when(gcmPushNotificationDaoSpy).sendNotificationToServer(any(Message.class), anyString());
PushNotificationResult result = gcmPushNotificationDaoSpy.sendPushNotification(message);
//verify business logic was correct based on Result
}
public Result buildFauxResult (String messageId, String canonicalRegistrationId, String errorCode) throws Exception {
Class <?> builderClass = Class.forName("com.google.android.gcm.server.Result$Builder");
Constructor <?> builderConstructor = builderClass.getDeclaredConstructors()[0];
ReflectionUtils.makeAccessible(builderConstructor);
Object builderObject = builderConstructor.newInstance();
Method canonicalRegistrationIdMethod = builderClass.getMethod("canonicalRegistrationId", String.class);
ReflectionUtils.makeAccessible(canonicalRegistrationIdMethod);
builderObject = ReflectionUtils.invokeMethod(canonicalRegistrationIdMethod, builderObject, canonicalRegistrationId);
Method messageIdMethod = builderClass.getMethod("messageId", String.class);
ReflectionUtils.makeAccessible(messageIdMethod);
builderObject = ReflectionUtils.invokeMethod(messageIdMethod, builderObject, messageId);
Method errorCodeMethod = builderClass.getMethod("errorCode", String.class);
ReflectionUtils.makeAccessible(errorCodeMethod);
builderObject = ReflectionUtils.invokeMethod(errorCodeMethod, builderObject, errorCode);
Method buildMethod = builderClass.getMethod("build");
ReflectionUtils.makeAccessible(buildMethod);
return (Result) ReflectionUtils.invokeMethod(buildMethod, builderObject);
}
Upvotes: 2