mardi 15 novembre 2016

Angular test multiple calls via $inject with deferred

Well I've spent a day or so looking for solutions to this and I don't know that I'm any the wiser, so I've broken down and decided to ask. Also, I'm fairly new to angular, so patience is appreciated.

I have a service that is periodically called from a controller to retrieve updated judge scores from a remote server. Fairly standard stuff. Only the relevant code is shown - without error handlers and interval cancelling, etc.

 $app.service('ResultService', function($http) {
        var self = this;
        self.results = {};

        self.getResults = function() {

            return $http.get('/results').then(function(response) {
                return $q.when(response.data).then(function(data) {
                    angular.copy(self.results, response.data);
                };
            };

        };
    });

    $app.controller('ResultController', function($interval, resultService) {
        var vm = this;

        $interval(function() {
            resultService.getResults().then(function(data) {
                vm.results = data;
            });
        }, 1000);
    });

All good so far, and everything works as desired in the browser.

Now to testing. I'd like to verify that updates are received and processed by the getResults function, however I'm struggling to perform the necessary chicken sacrifices to cause a deferment to be used more than once. I thought I could do something like this:

var deferred;
beforeEach(inject(function($q, httpBackend) {
    deferred = $q.defer();
    httpBackend.whenGET('/results').respond(deferred.promise);
});

it('updates results', inject(function($interval) {
    deferred.resolve({some: 'data'});
    scope.$digest();
    httpBackend.flush();
    $interval.flush(1200);
    // This works fine
    expect(resultsService.results).toEqual({some: 'data'});

    deferred.resolve({other: 'data'});
    scope.$digest();
    httpBackend.flush();
    $interval.flush(1200);
    // But this of course does not.
    expect(resultsService.results).toEqual({other: 'data'}); // fails
});

Now it appears that a deferred's promise can only be resolved once. So my question is, how do I provide fresh data to the service's $http.get() call with each tested iteration? I've tried various things, all without success:

  • Created a deferred that itself returned a deferred promise. This way madness lies.
  • Attempted to re-set the httpBackend.whenGET() with a new response. This had no effect.
  • Created a new deferred inside the test between the interval iterations. Needless to say this failed, since the original deferred's promise had already been set up.
  • Returned a static value from whenGET(), and manipulated that value directly. Unfortunately, while this "works", it seems rather hackish, and caused another test (one that expects a rejection) to fail.

So what am I missing? What's the trick here? I'm open to alternate solutions (not using deferreds) as well as anything that will clear the scales from my eyes with the code as-is.

Aucun commentaire:

Enregistrer un commentaire