39

Ich habe den folgenden Service bekommt:

angular.module("services") 
.factory("whatever", function($window) { 
    return { 
    redirect: function() { 
     $window.location.replace("http://www.whatever.com"); 
    } 
    }; 
}); 

Wie $window Objekt in Unit-Test verspotten Neuladen der Seite zu verhindern, wenn Tests ausgeführt werden?

I

spyOn($window.location, 'replace').andReturn(true);

mit

versucht, aber es hat nicht funktioniert (immer noch "Some of your tests did a full page reload!" Fehler) und

$provide.value('$window', {location: {replace: jasmine.createSpy()}})

, aber ich war immer einen Fehler (Error: [ng:areq] Argument 'fn' is not a function, got Object) mit Stack-Trace zeigt nur auf eckige eigene Quelle, so war es nicht sehr hilfreich ...

+0

Ich habe das gleiche Problem konfrontiert. Hast du eine Lösung gefunden? – Tushar

+0

Die Lösung, die von PaulL bereitgestellt wird, um '$ window.location' in einen separaten Dienst zu verpacken, funktioniert gut. Habe LostInComers Lösung noch nicht ausprobiert. – szimek

Antwort

54

In Chrome (nicht in anderen Browsern testen), location.replace ist schreibgeschützt, so dass SpyOn es nicht ersetzen konnte.

$provide.value sollte funktionieren. Irgendetwas muss irgendwo in deinem Code falsch sein.

Hier ist eine Arbeitseinheit Test

describe('whatever', function() { 
    var $window, whatever; 

    beforeEach(module('services')); 

    beforeEach(function() { 
     $window = {location: { replace: jasmine.createSpy()} }; 

     module(function($provide) { 
     $provide.value('$window', $window); 
     }); 

     inject(function($injector) { 
     whatever = $injector.get('whatever'); 
     }); 
    }); 

    it('replace redirects to http://www.whatever.com', function() { 
     whatever.redirect(); 
     expect($window.location.replace).toHaveBeenCalledWith('http://www.whatever.com'); 
    }); 
}); 
+4

Angular injiziert immer noch das eigentliche Fensterobjekt zu den Diensten – Tushar

+0

Bitte zeigen Sie uns Ihren Code – LostInComputer

+0

Hier ist ein [Arbeitscode] (https://dl.dropboxusercontent.com/u/14576915/window_test.zip), der die Antwort, die ich gepostet habe, demonstriert. Um zu laufen, 1. müssen Sie NodeJS haben 2. rufen Sie 'npm install' auf 3. rufen Sie 'grunt test' auf, um die Tests auszuführen – LostInComputer

0

Ich denke, was Sie wollen, ist die Verwendung der $location-Dienst, anstatt Anruf $window.location. Es gibt auch eine ganze Seite, die dieses Feature hier erklärt: http://docs.angularjs.org/guide/dev_guide.services.$location.

Mit diesem sollte es ziemlich einfach sein, eine Stub-Version des $ location Service in Ihren Tests zu verwenden.

+2

Danke, aber laut der Dokumentation: "Wenn Sie die URL ändern und die Seite neu laden oder zu einer anderen Seite navigieren müssen, verwenden Sie bitte eine niedrigere API, $ window.location.href." und genau das möchte ich - um auf eine externe URL umzuleiten. – szimek

+0

Ah, ich verstehe. Ich denke nicht, das ist möglich, aber das einzige, was ich mir vorstellen kann, wie das funktionieren wird, ist, das window.location-Objekt mit einem eigenen auszugeben. – calamari

4

Ich werde einen anderen Ansatz bieten, die für Sie arbeiten könnte. Ich hatte das gleiche Problem, als ich beim Unit-Testen eine Controlleraktion durchführte, die den Benutzer letztendlich umleitet (ganzseitige Auslastung, aber auf eine andere Seite in der größeren Website/Anwendung). Um einige Kontext zu geben, die Controller feuert einen AJAX-Request, und wenn die Antwort in Ordnung ist, wird der Benutzer zu einer anderen Seite über $ window.location.replace() umleiten:

$http.post('save', data) 
     .success(function(responseData, status, headers, config) { 
      if(responseData.redirect) { 
       $window.location.replace(responseData.redirect); 
      } 
     }) 
     .error(function(responseData, status, headers, config) { 
      console.error("ERROR while trying to create the Event!!"); 
     }); 

Der Test für diese Controller-Funktion verursacht das gleiche "Einige Ihrer Tests haben eine vollständige Seite neu geladen!" Error. So habe ich die folgenden auf die before() Funktion für die Controller-Spezifikation, den $ Fenster Service zu verspotten:

mockWindow = { location: { replace: function(url) { console.log('redirecting to: ' + url); } } }; 
eventCtrl = $controller('EventCtrl', { $scope: scope, $window: mockWindow }); 

Natürlich ist diese Lösung verhindert, dass ich (sauber) Überprüfung, ob die Ersetzen-Funktion aufgerufen wurde ein erwartetes Argument, aber das interessiert mich jetzt nicht wirklich .... Hoffe das hilft.

+1

Dies würde für Controller funktionieren, würde aber fehlschlagen, wenn Sie $ window in einen Dienst injizieren möchten. – Tushar

6

Ich gehe mit einer einfacheren, aber vielleicht weniger eleganten Lösung. Ich schreibe einen Wrapper für $ window.location, den ich dann vortäuschen kann. Wenn ich das auf deinen Code bezog, würde ich mich über die whatever.redirect-Funktion lustig machen, anstatt $ window zu verspotten (ich gehe hier davon aus, dass deine wirkliche Funktion komplexer ist).

Also würde ich am Ende mit:

angular.module("services") 
.factory("whatever", function($window) { 
    return { 
    do_stuff_that_redirects: function() { 
     lots of code; 
     this.redirect("http://www.whatever.com"); 
     maybe_more_code_maybe_not; 
    }, 

    redirect: function(url) { 
     $window.location.replace(url); 
    } 
    }; 
}); 

kann es dann direkt die Umleitung Methode spottet, und nur darauf vertrauen, dass, da es nur eine Zeile Code es nicht wirklich schief gehen kann.

spyOn(whatever, 'redirect').andCallFake(function(){}); 
expect(whatever.redirect).toHaveBeenCalledWith('http:/my.expected/url'); 

Dies ist für meine Zwecke ausreichend, und lässt mich die aufgerufene URL validieren.

0
$location.path('/someNewPath'); 

$ location.replace(); // oder Sie können diese verketten als: $ location.path ('/ someNewPath'). Replace();