Wednesday, May 31, 2017

Junit : Mocking a method of spy

I came across a test case failure in my project. The method mocked using spy was something like
public class Service {
 public String foo()
 {
  return "dummy";
 }
}

And in the test cases
private Service spyService;

@Test
public void test()
{
 String test = "test";
 when(spyService.foo()).thenReturn(test);
}

We did some changes in the Service to cache the reference values during initialization and use the map to get the values.

public class Service {
 private Map cache;
 ...
 public String foo()
 {
  return cache.get("someKey");
 }
}
And now the test cases started throwing NPE at cache.get() in Service's foo

Why is this? When you use spy, the method actually gets called. That's why before adding the cache, it was working fine.

So when you are using when on spy, call doReturn first. This will prevent the method being mocked from getting executed.

doReturn(test).when(spyService).foo();

Read this for more details.
Be careful when using spy in junit tests.

Friday, May 26, 2017

Cordova Push Plugin

I had an opportunity to work on an mobile application built on cordova. My hackathon project was about building a Notification Service and in order to integrate it to various channels, one of them being Mobile app, I worked on adding push plugin to the cordova app. For those who are new to cordova, it is "code in one language and deploy in many platforms" i.e. Platform independent. My JavaScript knowledge ranges from zero to none. But lucky for me, this change didn't involve much of JavaScript.

There were a few interesting review on exposing "too"much information on the notification payload. What is too much information depends on your business platform. So our initial thoughts of adding action buttons to the notification and hooking url to the action button on callbacks were all discarded due to the security risks. What I am going to outline here, is

- To get started with Cordova
- To send a push notification from FCM - Firebase Cloud Messaging and
- Receive it via Cordova Push Plugin

Installation

Follow the installation steps in the official Cordova documentation here
  • Install Node.js
  • Ìnstall GIT
  • Install Cordova - (I have tried the entire setup on windows)

Create a sample application

Since we are only going to try push notification, we don't need any fancy application here.

cordova create hello com.example.hello HelloWorld

Push plugin does not work in browser platform, So lets directly add android platform.

cd hello/
cordova platform add android

You can check the prerequisite for the building the platform.

cordova requirements

You would have to install Android SDK if you have not already. Follow this documentation. You would have to add java home, android home and path variables (both platform tools and tools folders)

export ANDROID_HOME=~/Android/Sdk

export PATH=$PATH:~/npm-global/bin

export PATH=$PATH:$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools

While installing Android SDK, if you face the issue where the gradle is not found under tools table.
http://stackoverflow.com/a/43027871 just download the tools lib and paste it in the location

And while installing emulator make sure the image has Google Play Service - any image greater than 4.2 is supposed to have Google Play Service


To check if your setup is fine, build and run it in emulator.

cordova build 

cordova run --emulator

Make sure your emulator is running before issuing the command. Boot up the "Hello World" application in the emulator app screen.

How does Push Notification work?

I cannot explain it better than this. Briefly,

There are Push Notification Providers such as FCM - Firebase Cloud Messaging (previously GCM - Google Cloud Messaging) APNS - Apple Push Notification Service

For these providers to send notification to your devices, 
  • Your device should register to providers. This will give your device an unique device registration key.
  • This registration key is sent to the server
  • Server asks the Notification Provider to send message to this registration key
This concept will be clear by the end of this post.

Setting up FCM

From this step you will need 
- Sender ID
- Server Key
  1. Go to FCM
  2. Go to Project settings
  3. Navigate to Cloud Messaging tab
GCM has been migrated to FCM. now if you already have your project setup done at GCM and the API key is already generated, then this setup will work just fine (Google has provided backward compatibility) But if you have not setup API Key yet, then you would have to migrate to FCM. Note that the Legacy Server Key in the screenshot is the one generated in GCM.

Sender ID in FCM is equivalent to the Project Number in GCM
Server Key in FCM is equivalent to the API Key in GCM

Let me briefly explain how the setup is in GCM. You can totally skip it if you are starting new with FCM
  1. Go to GCM
  2.  Project Number, the one followed by # is your sender ID
  3. API Key - created specifically for GCM, is your Server Key. Navigate to API Manager

Navigate to the Credentials in Left Panel. That API Key is your server key




At this point you should have Sender ID and Registration Key

Setting up Push Plugin in the Cordova App

cordova plugin add phonegap-plugin-push --variable SENDER_ID="SENDER_ID_HERE"

Navigate to hello/www/index.js
var app = {
// Application Constructor
initialize: function() {
console.log("device readyState")
 document.addEventListener("deviceready", this.onDeviceReady, false);
},

onDeviceReady: function() {
 var push = PushNotification.init({ "android": {"senderID": "88338672842"},
  "browser": {
  "pushServiceURL": 'https://push.api.phonegap.com/v1/push'
  },
  "ios": {"alert": "true", "badge": "true", "sound": "true"}, "windows": {} }
  );
 push.on('registration', function(data) {
  console.log("registration:");
  console.log(data.registrationId);
 });
 
 push.on('notification', function(data) {
  //console.log(data.title);
  console.log(data.message);
  // data.message,
  // data.title,
  // data.count,
  // data.sound,
  // data.image,
  // data.additionalData
 });

 push.on('error', function(e) {
  console.log("error");
  console.log(e.message);
 });
},
};
app.initialize();

During the application initialization, we are hooking the registration callback to deviceReady event.

Ideally you will send the registration Id to your server (to registration your user login with the registration key, which will be used further for sending push notification via providers by the server)

And then there is a notification callback - what should I do when the user taps on the notification or if the notification is received when the application is foreground. And there is an error handling callback as well.

Now rebuild the application.

cordova build android
cordova emulate android

Note that your emulator should already be running (Android SDK -> Tools -> Android -> AVD Manager). Navigate to the Hello World app.

We need the registration key that I printed in the registration callback. How to view the console log statements?

Navigate to your android_home/platform-tools.
adb logcat browser:V


Sending Push Notification

Now that we have the registration key, all we need to send message to this key. You can do it via python like detailed here

Or you can use a restful client and use the API here

I used PostMan
  • Add headers
    • Authorization is the sender id (format : key=sender_id)
    • set content-type to JSON
  • Send the request body
    • To - the registration key you got from the app

You will receive notification like:

you can check the console for the on Notification callback when you receive the notification while the app is on foreground


If you want the callback to work on the message tap, you need to set "content-available" in the notification payload.

And there are many more things to work on - logo, sounds, message stacking, etc. I would highly recommend you to  go through this documentation and decide what you want.

And note that you might have to do a lot of special handling depending on the device on which the app is installed - Android/IOS. You can make use of the device plugin to determine the the device type and also send this information to the server when you are registering the device key. This will help the server send different payload (as per android/ios) when they are using the provider to send push notifications.

Also, as the documentation mentions, you might notice there are double events when the content-available is set to 1. This post talks in detail about how to avoid that. Highlighting one particular comment.

if (data.additionalData.foreground) {

  /**

   * This block is reached when a push notification is received when the app is in foreground.

   * Depending on the design of your app, you might want to save the incoming data to the localstorage here.

   * That's not really what your question is about, but I thought mentioning it would give a better picture.

   */

}

else {

  if (data.additionalData.coldstart) {

    /**

     * This block is reached when a push notification is tapped on *AND*

     * the app is closed (ie. Not even in background).

     */

  }

  else {

    /**

     * The following block is reached in the following two scenarios:

     * 1. A push notification is tapped on *AND* the app is in background.

     * 2. A push notification with "content-available=1" is received while the app is in background. (iOS only)

     *

     * Therefore, for iOS devices, both of the above two scenarios can occur for an individual push notification.

     *

     * For Android, it's always the first scenario as the other one is not applicable.

     */



    // Here we check for the existence of the incoming data in the localstorage and, with that, we determine

    // which scenario of the two possible is happening.

    var pastPushSavedID = window.localStorage.getItem("pastPushSavedID");



    if (pastPushSavedID) {

      // `pastPushSavedID` is found in localStorage and thus this is scenario 1 mentioned above.

    }

    else {

      // `pastPushSavedID` is `null` and thus this is scenario 2 mentioned above.

    }

  }

}

A little bonus point. Try playing around with notification callback when
- app is foreground
- app is in background and running
- app is shut

Note that in all of these cases you are sending payload with content-available set

When I tried the callback worked for
- foreground
- app is shut (coldstart set to true)

But did not work for
- app is background and running

I found out I was not the only one. But I still do not have a solution for this. Post on the comments if  you face this as well.

Thursday, May 25, 2017

Functional Interfaces - Default and static methods

A cheat sheet is all that is needed to start using Java Lambdas. But here I will start with the design changes done in Java8 in order to accommodate Lambdas.

Most of the interfaces in the JDK have only method. (Comparator, Runnable, Callable, etc) These are Single Abstract Method interfaces. These SAM concepts are recreated in Java8 using Functional Interfaces. Since a lambda function can only provide implementation for one method, it is mandatory for your functional interface to have ONLY ONE interface

Functional Interface

Interface with only one abstract method.

Methods from the Object class do not count. i.e. The interface can also declare the abstract methods from the java.lang.Object class, but still the interface can be called as a Functional Interface. Example 

@FunctionalInterface

public interface SimpleFunctionalInterface {

  public void doWork();

  public boolean equals(Object o);

}


It can also have
- static methods 
- default methods

Note that the @FunctionalInterface is optional. It is just for convenience - the compiler can warn if it is not really a functional interface.

So what is the type of lambda expression? - Functional Interface
Is lambda expression an object? Well, it can be moved around like an object. An anonymous class is created with new. So I am explicitly asking JVM to create a new object (which is an overhead). but when I ask for a lamba, it is still recorded as an object in the JVM. But it is an object of a new kind in java8 - an object without an identity. I know this sounds complex. I will have a separate write-up on this. For now, the answer is no.

Default methods

In order to introduce lambda to Java 8, I need to add a forEach method to Iterable interface. Which will result in compilation error since all the classes that extends Iterable should provide an implementation for forEach. So I will be breaking the backward compatibility. 

Hence the default methods. A new Java8 concept to change the old interfaces without breaking the existing implementations. It provides flexibility to allow interface define implementation which will use as default in the situation where a concrete class fails to provide an implementation for that method. Meaning, they can be overridden. 

So what happens when the classes is implementing 2 interfaces with same default method signatures?

public interface InterfaceA {

    default void defaultMethod(){

        System.out.println("Interface A default method");

    }

}

public interface InterfaceB {

    default void defaultMethod(){

        System.out.println("Interface B default method");

    }

}

public class Impl implements InterfaceA, InterfaceB  {

}


So in these cases, to avoid compilation error, you have to provide your own implementation.
You can even choose to call either of the default methods like this:


public class Impl implements InterfaceA, InterfaceB {

    public void defaultMethod(){

        // existing code here..

        InterfaceA.super.defaultMethod();

    }

}

Summary:


When we extend an interface that contains a default method, we can perform following,
  • Override the default method similar to other methods we override in subclass..
  • Not override the default method and will inherit the default method.
  • Redeclare default method as abstract, which force subclass to override it.

Static Methods

The static methods in interfaces are good for providing utility methods.
Eg : isEquals in java.util.function.Predicate & identity in java.util.function.Function
We can’t define interface static method for Object class methods, we will get compiler error
as “This static method cannot hide the instance method from Object”. This is because
it’s not allowed in java, since Object is the base class for all the classes and we can’t have
one class level static method and another instance method with same signature.
These cannot be not overriden. if you think about it, it makes sense. Say I am providing a isNull
utility in my interface so that the implementations do it right, then I will prevent that
to be overridden
Now let us talk about the inheritance part a bit more. How about static methods in class?
public class SuperClass {
 public static void foo()
 {
  System.out.println("super class static");
 }
}

public class SubClass extends SuperClass{
 public static void foo()
 {
  System.out.println("sub class foo");
 }
}

public class App {
 public static void main(String[] args) {
  SuperClass.foo();
  SubClass.foo();
 }
}
This would have printed :
super class static sub class foo
What if there is no foo() in SubClass
public class SubClass extends SuperClass{
}
It still works :
super class static super class static
Now let us talk about static methods in interface
public interface SuperInterface {
 public static void foo()
 {
  System.out.println("super interface static");
 }
}
public interface SubInterface extends SuperInterface{
 public static void foo()
 {
  System.out.println("sub interface static");
 }
}

public class App {
 public static void main(String[] args) {
  SuperInterface.foo();
  SubInterface.foo();
 }
} 
Prints :
super interface static sub interface static
What if there is not foo in SubInterface? 
public interface SubInterface extends SuperInterface{
}

public class App {
 public static void main(String[] args) {
  SuperInterface.foo();
  SubInterface.foo(); -> compilation error }
}
Similarly, the following will work fine. Both class and interface can have static methods with same names, and neither overrides other
public class SomeClass implements SuperInterface{
 public static void foo()
 {
  SuperInterface.foo();
 }
}
public class App {
 public static void main(String[] args) {
  SomeClass.foo();
 }
}
but the moment you remove foo from SomeClass, it will result in compilation error
public class SomeClass implements SuperInterface{
}

public class App {
 public static void main(String[] args) {
  SomeClass.foo(); -> compilation error }
}
So why default can be inherited and static cannot? These are my favorite answer from stackoverflow.
1) A static method is a method that's associated with the class in which it's defined, rather than with any object created from that class. Every instance of the class shares the static methods of the class. Java 8 also lets static methods be defined in interfaces where they can assist default methods.
When you implement an interface that contains a static method, the static method is still part of the interface and not part of the implementing class. For this reason, you cannot prefix the method with the class name. Instead, you must prefix the method with the interface name
2) Static methods in interfaces could create a diamond of death if they were being inherited. So, calling a static method from the appropriate interface is good enough compared to the risk of calling it from a concrete class that may implement multiple interfaces that contain static methods of the same name.
Why are static methods any different?
Static methods are just functions unrelated to the objects. Instead of placing them in utility abstract classes (like calling Collections.sort() ) we move those functions (static methods) to their appropriate interfaces. They could be bound to the inherited objects like the default methods do, but that is not their job. Static methods provide functionality which is unrelated to the instances of the class.
Example:
interface Floatable {

    default void float() {
        // implementation
    }

    static boolean checkIfItCanFloat(Object fl) {
         // some physics here
    } 
}

class Duck implements Floatable { }
So, the point is that a Duck may float but the function that checks if an Object really floats is not something that a Duck can do. It is an irrelevant functionallity that we could pass to our Floatable interface instead of having it sit inside some utility class.

References :

Thursday, May 18, 2017

Parameterized Junit test

I have used the Parameterized Junit test in places where you need to test a method with huge combinations of input. Imagine a truth table logic in place.
Form the truth table -> let's take 2 inputs for simplicity sake.
A
B
Expected
1
1
1
1
0
1
0
1
1
0
0
0

Say if the method is,
Method(param1, param2)
Now if I have to go with the conventional junit test cases, I would have to name my methods like,
  • testMethodForParam1TrueParam2False()
  • testMethodForParam1TrueParam2True()
  • testMethodForParam1FalseParam2True()
  • testMethodForParam1FalseParam2False()
Sure it looks readable now. But consider the same for a 3 X 3 matrix. It becomes ugly.
Wouldn't it better to actually have this truth table in the start of the test case and feed it to just one test?
This is exactly what Parameterized Junit solves.
There are various blogs and sources to refer the syntax of such test.
But when you want to use it in an integration test, you need to keep the following in mind.
  1. As long as your data is static, you have nothing to worry about. Say, your service which contains the method, is initialized in @BeforeClass and in your @Parameterized.Parameters the data you feed is coming from a static method in that service. Then you will get NPE. Because @Parameters is called before @BeforeClass. To tackle this, have your initialization in a simple static block. But then, you need to make the test class final. @BeforeClass will apply to all the inherited classes, static block will not.
    package com.foo;
    
    import static org.junit.Assert.assertEquals;
    import static org.junit.Assert.assertNotNull;
    
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.Date;
    import java.util.List;
    import java.util.Optional;
    
    import org.junit.Before;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.junit.runners.Parameterized;
    
    
    @RunWith(Parameterized.class)
    public final class ServiceTest {
     private static Service service;
    
     private Integer param1;
     private Integer param2;
     private Integer expectedResult;
     private String caseName;
    
     static {
     setUpClass();
     }
    
     public static void setUpClass() {
     service = (Service) CustomContextLoader.getBean("service");
     }
    
     public ServiceTest(String caseName, Integer param1, Integer param2, Integer expectedResult) {
     this.caseName = caseName;
     this.param1 = param1;
     this.param2 = param2;
     this.expectedResult = expectedResult;
     }
    
     @Parameterized.Parameters
     public static Iterable<Object[]> testData()
     {
     return Arrays.asList(new Object[][]{
     { "case 1 : 1 1 = 1", 1, 1, 1},
     { "case 2 : 1 0 = 1", 1, 0, 1},
     { "case 3 : 0 1 = 1", 0, 1, 1},
     { "case 4 : 0 0 = 0", 0, 0, 0},
     });
    
     }
    
     @Before
     public void setup() {
     System.out.println("@Before");
     }
    
     @Test
     public void testMethod() {
     Integer result = service.orMethod(this.param1, this.param2);
     assertEquals(this.caseName+" failed", this.expectedResult, result);
     }
    
    }
    Remember the order of execution
    Static block
    @Parameter
    @BeforeClass
    @Before
    @Test
    For my case, I had to bootstrap some data (think of inserting some rule and referencing that throughout your test case using an ID. In such case I need to insert the rule and get the ID before I can setup the data) before I can set test data. Hence it was important for the service to be initialized before it hit the @Parameters method. I could not use @BeforeClass because it gets executed after @Parameters. Hence I had to resort to static method.
  2. Add a description for each test data you setup in the @Parameters. and add this to the assertion string. This will help identify which dataset is failing. Because, since this is parameterized test, the test results would be printed something like this:
    com.foo.ServiceTest > testMethod[0] PASSED
    com.foo.ServiceTest > testMethod[1] PASSED
    com.foo.ServiceTest > testMethod[2] FAILED
    java.lang.AssertionError
    com.foo.ServiceTest > testMethod[3] PASSED
    Now if you have setup a caseName with the testData, you will spend less time debugging the issue.
    com.foo.ServiceTest > testMethod[0] PASSED
    com.foo.ServiceTest > testMethod[1] PASSED
    com.foo.ServiceTest > testMethod[2] FAILED
    java.lang.AssertionError : case 3 : 0 1 = 1 failed
    com.foo.ServiceTest > testMethod[3] PASSED
    Also note that the parameterized junit test runs like there are separate @Test methods for all of those data sets. Means it will continue running all the tests even if one fails. It is not fail-fast.

KeySet And UnsupportedOperationException

package com.foo;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.junit.Test;

public class SomeTest {
 @Test
 public void test() {
 Map<String, Set<String>> movieToActorsName = new HashMap<>();
 Set<String> actors = new HashSet<>();
 actors.add("Dan");
 actors.add("Emma");
 movieToActorsName.put("HP", actors);
 Map<String, Set<String>> someMap = new HashMap<>();
 someMap.put("Movies", new HashSet<>(movieToActorsName.keySet()));
 someMap.values().stream().forEach(s -> {
 s.add("dummy");
 });
 someMap = new HashMap<>();
 someMap.put("Movies", movieToActorsName.keySet());
 someMap.values().stream().forEach(s -> {
 s.add("dummy"); // UnsupporrtedOperationException. Because keySet() is immutable.
 });
 }
}
Keyset is basically just a view of the keys in the map. So If you call remove() on the keyset, it will actually remove the entry for that key in the map.
I found this common where people directly take keySet of a map into a placeholder, which will be modified in the later part of the code. The following is wrong.
placeHolderMap.put(id, referenceMap.keySet());
And this is right.
placeHolderMap.put(id, new HashSet<>(referenceMap.keySet()));
or
placeHolderMap.put(id, referenceMap.keySet().stream().collect(Collectors.toSet()));