2016-07-29 12 views
3

Ich versuche, eine Antwort auf eine JSONP GET-Anfrage zu verspotten, die mit einer Funktion gemacht wird, die eine ES6-Versprechen zurückgibt, die ich in $q.when() verpackt habe. Der Code selbst funktioniert gut, aber in den Unit-Tests wird die Anfrage nicht von $ httpBackend abgefangen und geht direkt zur eigentlichen URL über. Wenn also flush() aufgerufen wird, erhalte ich einen Fehler unter der Nummer Error: No pending request to flush !. Die JSONP-Anforderung wird über die $.getJSON()-Komponente von jQuery im ES6-Versprechen gestellt. Daher habe ich mich dafür entschieden, alle ausgehenden Anforderungen abzufangen, indem ich eine Regex anstelle einer fest codierten URL zur Verfügung stelle.

Ich habe überall auf der Suche nach diesem herauszufinden für eine Weile jetzt und immer noch zu verstehen, was den Anruf verursacht, durch zu gehen. Ich habe das Gefühl, dass die HTTP-Anfrage im ES6-Versprechen "außerhalb von Angular" gemacht wird, so dass $ httpBackend nichts davon weiß/nicht in der Lage ist, es zu fangen, obwohl das nicht der Fall ist, wenn der Anruf getätigt wurde innerhalb eines $ q Versprechens von Anfang an. Kann mir jemand vielleicht sagen, warum dieser Anruf durchkommt und warum eine einfache Zeitüberschreitung gut funktioniert? Ich habe alle Kombinationen von $scope.$apply, $scope.$digest und hier versucht, aber ohne Erfolg.

Vielleicht besser einige Code wird es erklären ...

-Controller

function homeController() { 
    ... 
    var self = this; 
    self.getData = function getData() { 
     $q.when(user.getUserInformation()).then(function() { 
      self.username = user.username; 
     }); 
    }; 
} 

Einheit Test

... 

beforeEach(module('home')); 

describe('Controller', function() { 
    var $httpBackend, scope, ctrl; 

    beforeEach(inject(function(_$httpBackend_, $rootScope, $componentController) { 
     $httpBackend = _$httpBackend_; 
     scope = $rootScope.$new(); // used to try and call $digest or $apply 
     // have also tried whenGET, when('GET', ..), etc... 
     $httpBackend.whenJSONP(/.*/) 
        .respond([ 
         { 
          "user_information": { 
           "username": "TestUser", 
          } 
         } 
        ]); 
     ctrl = $componentController("home"); 
    })); 

    it("should add the username to the controller", function() { 
     ctrl.getData(); // make HTTP request 
     $httpBackend.flush(); // Error: No pending request to flush ! 
     expect(ctrl.username).toBe("TestUser"); 
    }); 
}); 

... 

Aus irgendeinem Grund funktioniert dies jedoch:

it("should add the username to the controller", function() { 
     ctrl.getData(); // make HTTP request 
     setTimeout(() => { 
      // don't even need to call flush, $digest, or $apply...? 
      expect(ctrl.username).toBe("TestUser"); 
     }); 
    }); 
+1

httpBackend ist eine Mock-Implementierung, die in die $ HTTP-Dienst durch Winkel für die Unit-Tests eingespritzt wird, wenn Sie nicht den $ http-Dienst verwenden zu machen Bei einer Anfrage können Sie das httpBackend nicht verwenden, um es abzufangen –

Antwort

0

Dank Grahams Kommentar wurde ich weiter unten einen verschiedenen Kaninchenbau gebracht aufgrund meines mangels mehr Dinge zu verstehen, die ich hier in dem Fall zusammenfassen werde jemand endet in der gleichen Situation up ...

  1. Ich habe nicht ganz verstanden, wie JSONP funktioniert. Es verlässt sich überhaupt nicht auf XmlHttpRequest (siehe here). Anstatt zu versuchen, mit spöttischen Antworten auf diese Anfragen durch JSONP herumzuspielen, habe ich einfach das "Debug" -Flag auf den Code gesetzt, den ich benutzte, der JSONP deaktivierte, so dass die Aufrufe dann über XHR-Objekte gemacht wurden (dies würde die same origin policy fehlschlagen, wenn echte Antworten waren benötigt von dieser externen API).
  2. Anstatt zu versuchen, Jasmine-Ajax zu verwenden, habe ich einfach einen Spion auf jQuerys getJSON gesetzt und eine Scheinantwort zurückgegeben. Dies schickte schließlich die verspottete Antwort auf die ES6-Zusage, aber aus irgendeinem Grund wurde die then-Funktion des $ q-Zusicherungsobjekts, das aus dem Umbrechen der ES6-Zusage resultierte, nicht aufgerufen (noch andere Fehlerbehandlungsfunktionen, selbst finally). Ich habe auch versucht, $scope.$apply() so ziemlich überall anzurufen, wenn es nicht helfen würde, aber ohne Erfolg.Früher habe ich # 2-Implementierung, die Daten zu senden und nur

    // user.getUserInformation() returns an ES6 promise 
    $q.when(user.getUserInformation()).then(function() { 
        // this was never being called/reached! (in the unit tests) 
    }); 
    
  3. sie:

    Grundausführung (in Unit-Test):

    ... 
    spyOn($, 'getJSON').and.callFake(function (url, success) { 
        success({"username": "TestUser"}); // send mock data 
    }); 
    ctrl.getData(); // make GET request 
    ... 
    

    Problem (in der Quelle des Controllers) Wrapped die Assertions im Komponententest innerhalb eines Timeouts ohne angegebene Zeitdauer. Mir ist klar, dass das nicht optimal ist und hoffentlich nicht so ist, aber nach vielen Stunden habe ich mein Limit erreicht und aufgegeben. Wenn jemand eine Idee hat, wie man dies verbessern kann, oder warum then nicht aufgerufen wird, würde ich es wirklich lieben, es zu hören.

    Einheit Test:

    ... 
    ctrl.getData(); // make GET request 
    setTimeout(() => { 
        expect(ctrl.username).toBe("TestUser"); // works! 
    }); 
    
Verwandte Themen