user2836797
user2836797

Reputation:

How can I mock a message sent from Google Cloud Messaging?

I am using Google Cloud Messaging API to send push notifications to Android devices. I have implemented the IntentService and corresponding logic to handle notifications from the GCM server. The problem is that GCM sometimes takes up to fifteen minutes to dispatch messages, and it has made debugging a huge pain.

I have searched for how to mock GCM, but did not find any solutions that apply to my situation. I already have the third-party client server implemented; the problem is waiting for GCM to actually dispatch the messages to the Android device.

The entry point on the Android device is the IntentService that has a hook method handleIntent(Intent). It seems like one possibility would be to write another program that sends a "spoofed" intent to the system, such that the system loads my IntentService with an intent that acts, looks and feels like an authentic GCM intent. That way, my apps could receive messages instantaneously.

Has anybody encountered this issue, or have any tips on how to solve it?

Upvotes: 4

Views: 2896

Answers (2)

Chantell Osejo
Chantell Osejo

Reputation: 1536

I use Postman to fake the notification.

You'll need headers:

// Please note that the authorization header's KEY is actually "key=<your GCM API key>"
Authorization: key=<your GCM API key> 
Content-Type: application/json

Then POST to https://android.googleapis.com/gcm/send (this is for Android, I'm assuming that somewhere out there there is also an iOS one now that Google is supporting iOS devices as well).

Your body must look like this:

{       
  "registration_ids":["<Your device registration token from GCM>"],
  "data": {
    "message" : "your message here"
  }
}

I'm assuming (but I have not confirmed):

  1. You can do a comma-separated list on the registration_ids
  2. You can put other fields inside that "data" json; any that might be passable via a Bundle. This is based on the fact that this code works:

    public class PushNotificationListenerService extends GcmListenerService {
       private static final String TAG = "NotificationListener";
    
       /**
        * Called when message is received.
        *
        * @param from SenderID of the sender.
        * @param data Data bundle containing message data as key/value pairs.
        *             For Set of keys use data.keySet().
        */
        // [START receive_message]
        @Override
        public void onMessageReceived(String from, Bundle data) {
           // Pay attention to this line of code; this indicates
           // that you could have ANY key-value pair passed in
           // as long as it's capable of being serialized into a Bundle object
           String message = data.getString("message");
           Log.d(TAG, "From: " + from);
           Log.d(TAG, "Message: " + message);
    
            /**
             * In some cases it may be useful to show a notification 
             * indicating to the user that a message was received.
             */
            sendNotification(message);
    
            //TODO any of your own logic to handle the notification
        }
        // [END receive_message]
    
        /**
         * Create and show a simple notification containing the received GCM        
         * message.
         *
         * @param message GCM message received.
         */
        private void sendNotification(String message) {
           Intent intent = new Intent(this, MainActivity.class);
           intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
           PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 
               /* Request code */, intent,
               PendingIntent.FLAG_ONE_SHOT);
    
           Uri defaultSoundUri =      
             RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
             NotificationCompat.Builder notificationBuilder = new  
             NotificationCompat.Builder(this)
                .setSmallIcon(R.drawable.ic_favorite_white_shadow_36dp)
                .setContentTitle("GCM Message")
                .setContentText(message)
                .setAutoCancel(true)
                .setSound(defaultSoundUri)
                .setContentIntent(pendingIntent);
    
            NotificationManager notificationManager =
               (NotificationManager)    
               getSystemService(Context.NOTIFICATION_SERVICE);
    
            notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
        }
    }
    

Upvotes: 1

Daan
Daan

Reputation: 2898

If you want to mock something and you do not know how to do this, use the following approach.

  1. Make an adapter class (CAdpapter) with the dependency you need to mock (the intentservice).
  2. Make a few public methods that call the dependancy.
  3. Make an interface (IAdapater), and make sure the adapter class implements this interface (just put the methods you created in step 2 in this interface).
  4. Make sure that classes that need to talk to the dependancy (as said the intentservice) do not do this directly, they talk to an instance of IAdapter.
  5. Write a mock class that implements IAdapter (MockAdapter). If you do not like this, use a mocking framework.

Classes can now talk to the intentservice with the adapater or to a Mock instead. A solution can be to make your own class that talks to the dependancy you need to mock.

Mocking can be hard. A Mock needs to implement the same interface as the normal class. However, if this class has a huge interface or no interface at all than it can be a problem (these are just examples). Writing your own class that calls the class you need to mock can be a solution

Upvotes: 3

Related Questions