Conditional Injection – Testing Angular App on Android

I was developing an Angular-based app inside an Android app. That is, my Android Activity incorporated a WebView that loaded HTML/JavaScript pages that use Angular.

The Java code in the Andorid app communicated to with the JavaScript running inside the WebView by injecting a Java object into JavaScript as discussed in a previous post.

Cool! So?
The problem arose when I wanted to test the app. One could simply test the app within the Android IDE (Eclipse in this case), but the development cycle is much faster if I could just use a browser as one would normally do when building a web app. But the JavaScript depends on the injected Java objects in order to function, so how can I test my app in the browser in absence of the Java code that injects these objects.

I embarked on the following solution:

  • Encapsulate each of the injected Java objects in a “wrapper” Angular service.
  • Also create Angular services that mock each of the wrapper services.
  • When running in Android instantiate the wrapper services, otherwise instantiate the mock ones.
The trick here is that both services, the wrapper and corresponding mock, have the same name and only one of them can be instantiated in a particular run. Since there is no built in conditional compilation/inclusion directive akin to that of C, I came up with my own using a directive that looks like this:
<script conditional-load
        if-defined=""
        else-load=""
        then-load=""/>
The core code for the directive checks whether the specified reference is defined and loads either script accordingly.
myModule.directive("conditionalLoad", function($window, ScriptLoaderService) {

 return {
     link: function(scope, element, attrs, controller) {
         ...
         if ($window[attrs.ifDefined]) {
            ScriptLoaderService.loadScript(attrs.thenLoad);
         }
         else if (attrs.elseLoad) {
            ScriptLoaderService.loadScript(attrs.elseLoad);
         }
     }
 }
})
With an instance of this directive for every injected Java object we are left with writing the ScriptLoaderService, which turns out to be non-trivial, and I will leave this to a separate post.