How is esriLoader (angular-esri-map) intended to be used in karma tests and what is its true purpose?

5339
5
04-21-2016 06:32 AM
JoshWerts
New Contributor III


I'm trying to understand the esriLoader utility in angular-esri-map.  I'm not completely clear on the purpose of using it instead of dojo's require directly.  Furthermore, I've been completely unable to figure out how to use it in karma/mocha tests.  Specifically, the promises never return and mocha times out.  Previously, i was using the karma-dojo plugin and calling dojo's require directly which seems to work.  My big assumption here is that esriLoader should allow me to ditch the karma-dojo plugin.

I've tried many iterations of this without any luck (including some attempts to add in $rootScope.$apply() calls).  Here's just one of them:

describe('pointsModel spec', function() {
  var pointsModel;
  var esriLoader;

  beforeEach(module('jsapi4'));
  beforeEach(module('esri.core'));

  beforeEach(inject(function(_pointsModel_, _esriLoader_) {
    pointsModel = _pointsModel_;
    esriLoader = _esriLoader_;
  }));

  var Collection;
  beforeEach(function(done) {
    esriLoader.bootstrap().then(function() {
      esriLoader.require([
        "esri/core/Collection"
      ], function(_Collection) {
        Collection = _Collection;
        done();
      });
    });
  });

  describe('construction', function() {
    it('should inject pointsModel', function() {
      expect(pointsModel).to.not.be.undefined;
    });
  });

  describe("summary", function() {
    var points;

    beforeEach(function() {
      points = new Collection();
      pointsModel.setPoints(points);
    });

    it('should count indexes', function() {
      pointsModel.addPoints([
        { attributes: {index: 1}},
        { attributes: {index: 2}}
      ]);
      expect(pointsModel.getIndexSum()).to.equal(3);
    });
  });
});
0 Kudos
5 Replies
TomWayson
Esri Contributor

Good question!


The true purpose of the esriLoader is to wrap dojo's `require()` in $q promises, which automatically handle updating Angular's digest cycle to notify Angular that something async has happened, namely that you got the esri modules you requested and that you exectued some code in a callback or `then()`.

In terms of testing, I would say that you should be mocking esriLoader and/or the modules it would return and only test your own code. You can use karma-dojo the way I have karma configured here to actually load the modules, but I would advise against that. I may be less work than mocking those dependencies, but it introduces external dependencies and complexity into your tests that will make them slower and less reliable.

0 Kudos
JoshWerts
New Contributor III

Tom - thanks for the response.  I understand that $q keeps us in the digest cycle (at least I understand the concept and have seen that with other cases - haven't specifically wrapped my head around it in terms of the esriLoader).

I'm still a bit confused on testing without those dependecies.  For example, are you saying that you really should have mocked esri.geometry.Polygon somehow in your test here?  esri-karma-tutorial/geometryUtilsSpec.js at master · tomwayson/esri-karma-tutorial · GitHub

0 Kudos
TomWayson
Esri Contributor

If that were a real app, and not a demonstration of how to use karma-dojo to actually load Esri modules instead of mocking them, then yes, the best practice would be to have mocked Polygon there.

Sent from Outlook Mobile<https://aka.ms/blhgte>

0 Kudos
TomWayson
Esri Contributor

To add some context, when I developed that tutorial, we were trying to unit test widgets, so the average `define()` block had about 10+ dependencies from dojo, dijit, esri, etc. That would be too much to mock. A better pattern would have been to isolate the business logic code into utility modules with few or 0 dependencies, which would be easier to test in isolation via mocks. Confusingly, the utility modules I used as examples in that tutorial actually would be a good candidates for mocking, b/c they have so few dependencies. If I were to go back and do it again, I'd do one example of testing a widget w/o mocks, and one testing a utility module w/ mocks. I'm not actively developing that tutorial any more, but PRs are welcome!


In an angular application, I would advocate mocking all the time. First, I'd expect that the only AMD modules you're loading are esri modules for the mapping functionality, b/c for all other functionality (DOM, arrays, etc) you're probably relying on Angular. Angular makes it easy to mock those thanks to DI and ngMock. So you should only have to mock only the esri modules needed by each of your own modules. That way your tests never have to call Dojo's `require()`, so you don't need karma-dojo, or any other means of loading Dojo modules.

HTH

JoshWerts
New Contributor III

Ok, this is all great information and I really appreciate the detailed response.  I'm totally with you on

I'd expect that the only AMD modules you're loading are esri modules for the mapping functionality, b/c for all other functionality (DOM, arrays, etc) you're probably relying on Angular

This makes perfect sense.  I'm also already isolating into small modules and mocking server responses with sinon in the case of service modules w/ REST calls which makes sense to me.

I still don't really understand how/why you would mock an esri Polygon object when your function accepts a Polygon as an input, but I'm still learning when it comes to unit-testing best practices.

What would be awesome is if the angular-esri-map repo included an example that showed unit testing best practices.  This seems to always be missing from the GIS community, and adding in the map and Dojo make things quite a bit harder to sort out.

0 Kudos