Joe
Joe

Reputation: 2987

Java Server side UnitTesting GCM's Result

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

Answers (2)

ShatyUT
ShatyUT

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

Joe
Joe

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

Related Questions