AngularJS: Unit testing that involves HTML templates

The AngularJS app I work on uses Angular-UI dialogs that load HTML templates for the different Boostrap dialogs used in the app. Dialogs are provided as a service and in my opinion while providing a nice wrapper around the dialog functionality provided by Bootstrap, violates the separation of concern between services and directives enforced by AngularJS but that’s another issue. While testing logic in my service that uses the $dialogProvider i started getting a classic Unexpected Request error while running my unit tests for the particular service:

Error: Unexpected request: GET /myapp/js/angular/app/views/partials/dialogs/request.html
No more request expected
at Error (<anonymous>)

It became clear that the issue was related to the a function in my service API trying to load the dialog’s template and failing to do so. Since I am using Yeoman as a workflow tool which includes Karma (formerly Testacular) as the testing framework, i dug around a little bit and found out that Karma provides you with an HTML preprocessing tool called Html2JS. From the AngularJS point of view, all it does it cache each HTML template using $templateCache under a key made of the path to the file.  Setting it up is pretty simple:

  • Include in your karma.conf.js file, include the path to your html files in the files config.

    files=[‘app/views/**/*.html’]

  • In the same file, add the preprocessor command

    preprocessors = {
    ‘app/views/**/*.html’: ‘html2js’

    };

  • From the Karma point of view, your setup is complete. You now need include the appropriate template in your unit test file. Declare the dependency in a beforeEach statement as in:
    beforeEach(module(‘app/views/partials/dialogs/request.html’));
  • If your application is declared in a standard way, meaning your files are served as defined in the configuration path i.e ‘app/views/**/*.html’ you are good, but in my particular case, as you can see from the error i got my actual request path is ‘/myapp/js/angular/app/views/partials/dialogs/request.html’ (a root relative URL), which from the point of view of the preprocessor and cache does not exist. I used to little trick to make it work. Here is my unit test service set up method:

    beforeEach(inject(function (_requestService_, $httpBackend,$rootScope, $templateCache) {
    requestService = _requestService_;
    rootScope=$rootScope;
    $templateCache.put(‘/myapp/js/angular/app/views/partials/dialogs/request.html’, $templateCache.get(‘app/views/partials/dialogs/request.html’))
    }));

  • As you can see I just added a new cache entry in $templateCache using the requested path as a key and the preprocessor-defined path as the key for my value since it is initially the template I am trying to load. If that’s you case you should be good to go and ready to hit the road at this point.

NB: There is a closed issue in the Html2Js preprocessor repo that allows a transformation of the cache key that would also resolve the issue I ran into but I could figure out how to set it up in my project.