AngularJS ist unter Berücksichtigung der Testbarkeit ausgelegt. Die Abhängigkeitsinjektion ist eines der herausragenden Merkmale des Rahmens, das die Einheitstests erleichtert. AngularJS definiert einen Weg, die Anwendung ordentlich zu modularisieren und in verschiedene Komponenten wie Controller, Richtlinien, Filter oder Animationen zu unterteilen. Dieses Entwicklungsmodell bedeutet, dass die einzelnen Teile isoliert funktionieren und die Anwendung über einen langen Zeitraum leicht skalieren kann. Wenn die Erweiterbarkeit und Testbarkeit Hand in Hand gehen, ist es einfach, den AngularJS-Code zu testen.
Gemäß der Definition von Unit -Tests sollte das zu testende System isoliert getestet werden. So müssen alle vom System benötigten externen Objekte durch Scheinobjekte ersetzt werden. Wie der Name selbst sagt, führen die Scheinobjekte keine tatsächliche Aufgabe aus. Vielmehr werden sie verwendet, um die Erwartungen des untersuchten Systems zu erfüllen. Wenn Sie eine Auffrischung über das Verspotten benötigen, lesen Sie bitte einen meiner vorherigen Artikel: Verspottungsabhängigkeiten in AngularJS -Tests.
In diesem Artikel werde ich eine Reihe von Tipps zu Testdiensten, Controllern und Anbietern in AngularJs teilen. Die Codeausschnitte wurden mit Jasmine geschrieben und können mit dem Karma -Testläufer ausgeführt werden. Sie können den in diesem Artikel verwendeten Code von unserem Github -Repo herunterladen, in dem Sie auch Anweisungen zum Ausführen der Tests finden.
Dienste sind eine der häufigsten Komponenten in einer AngularJS -Anwendung. Sie bieten eine Möglichkeit, die wiederverwendbare Logik an einem zentralen Ort zu definieren, sodass man nicht die gleiche Logik immer wieder wiederholen muss. Die Singleton -Natur des Dienstes ermöglicht es, dieselben Daten über mehrere Controller, Richtlinien und sogar andere Dienste hinweg zu teilen.
Ein Dienst kann von einer Reihe anderer Dienste abhängen, um seine Aufgabe auszuführen. Ein Dienst mit dem Namen A hängt von den Diensten B, C und D ab, um seine Aufgabe auszuführen. Beim Testen des Dienstes A müssen die Abhängigkeiten B, C und D durch Mocks ersetzt werden.
Wir verspotten im Allgemeinen alle Abhängigkeiten, mit Ausnahme bestimmter Versorgungsdienste wie $ rootscope und $ parse. Wir erstellen Spione für die Methoden, die in den Tests (in Jasmin, Mocks als Spione) unter Verwendung von Jasmine.Createspy () inspiziert werden müssen.
Betrachten wir den folgenden Service:
angular<span>.module('services', []) </span> <span>.service('sampleSvc', ['$window', 'modalSvc', function($<span>window, modalSvc</span>){ </span> <span>this.showDialog = function(message<span>, title</span>){ </span> <span>if(title){ </span> modalSvc<span>.showModalDialog({ </span> <span>title: title, </span> <span>message: message </span> <span>}); </span> <span>} else { </span> $<span>window.alert(message); </span> <span>} </span> <span>}; </span> <span>}]);</span>
Um Muster zu testen
Jetzt können wir das Verhalten der Showdialog -Methode testen. Die beiden Testfälle, die wir für die Methode schreiben können, sind wie folgt:
<span>var mockWindow, mockModalSvc, sampleSvcObj; </span><span>beforeEach(function(){ </span> <span>module(function($provide){ </span> $provide<span>.service('$window', function(){ </span> <span>this.alert= jasmine.createSpy('alert'); </span> <span>}); </span> $provide<span>.service('modalSvc', function(){ </span> <span>this.showModalDialog = jasmine.createSpy('showModalDialog'); </span> <span>}); </span> <span>}); </span> <span>module('services'); </span><span>}); </span> <span>beforeEach(inject(function($<span>window, modalSvc, sampleSvc</span>){ </span> mockWindow<span>=$window; </span> mockModalSvc<span>=modalSvc; </span> sampleSvcObj<span>=sampleSvc; </span><span>}));</span>
es ruft wachsam auf, wenn kein Titel Parameter in
Diese Methode hat nicht viel Logik zu testen, während die Dienste in typischen Web -Apps normalerweise eine Menge Funktionen enthalten würden. Sie können die in diesem Tipp gezeigte Technik verwenden, um die Verweise auf Dienste zu verspotten. Die Service -Tests sollten jedes mögliche Szenario abdecken, das beim Schreiben des Dienstes angenommen wurde.
<span>it('should show alert when title is not passed into showDialog', function(){ </span> <span>var message="Some message"; </span> sampleSvcObj<span>.showDialog(message); </span> <span>expect(mockWindow.alert).toHaveBeenCalledWith(message); </span> <span>expect(mockModalSvc.showModalDialog).not.toHaveBeenCalled(); </span><span>}); </span> <span>it('should show modal when title is passed into showDialog', function(){ </span> <span>var message="Some message"; </span> <span>var title="Some title"; </span> sampleSvcObj<span>.showDialog(message, title); </span> <span>expect(mockModalSvc.showModalDialog).toHaveBeenCalledWith({ </span> <span>message: message, </span> <span>title: title </span> <span>}); </span> <span>expect(mockWindow.alert).not.toHaveBeenCalled(); </span><span>});</span>
Fabriken und Werte können auch mit derselben Technik getestet werden.
Test -Controller
Da die Controller im Allgemeinen an eine Ansicht gebunden sind, hängt das Verhalten von Methoden in den Controllern von den Ansichten ab. Außerdem können einige zusätzliche Objekte nach dem Zusammenstellen der Ansicht zum Zielfernrohr hinzugefügt werden. Eines der häufigsten Beispiele dafür ist ein Formobjekt. Um die Tests wie erwartet funktionieren zu lassen, müssen diese Objekte manuell erstellt und dem Controller hinzugefügt werden.
Ein Controller kann von einer der folgenden Typen sein:
Wenn Sie sich nicht sicher sind, können Sie hier mehr darüber lesen. In beiden Fällen werden wir diese beiden Fälle diskutieren.
Betrachten Sie den folgenden Controller:
angular<span>.module('services', []) </span> <span>.service('sampleSvc', ['$window', 'modalSvc', function($<span>window, modalSvc</span>){ </span> <span>this.showDialog = function(message<span>, title</span>){ </span> <span>if(title){ </span> modalSvc<span>.showModalDialog({ </span> <span>title: title, </span> <span>message: message </span> <span>}); </span> <span>} else { </span> $<span>window.alert(message); </span> <span>} </span> <span>}; </span> <span>}]);</span>
Um diesen Controller zu testen, müssen wir eine Instanz des Controllers erstellen, indem wir ein $ Scope -Objekt und ein verspottetes Objekt des Dienstes (DATASVC) übergeben. Da der Dienst eine asynchrone Methode enthält, müssen wir darüber verspotten, dass ich die spöttische Versprechen -Technik, die ich in einem früheren Artikel beschrieben habe.
Der folgende Snippet verspottet den DataSVC -Dienst:
<span>var mockWindow, mockModalSvc, sampleSvcObj; </span><span>beforeEach(function(){ </span> <span>module(function($provide){ </span> $provide<span>.service('$window', function(){ </span> <span>this.alert= jasmine.createSpy('alert'); </span> <span>}); </span> $provide<span>.service('modalSvc', function(){ </span> <span>this.showModalDialog = jasmine.createSpy('showModalDialog'); </span> <span>}); </span> <span>}); </span> <span>module('services'); </span><span>}); </span> <span>beforeEach(inject(function($<span>window, modalSvc, sampleSvc</span>){ </span> mockWindow<span>=$window; </span> mockModalSvc<span>=modalSvc; </span> sampleSvcObj<span>=sampleSvc; </span><span>}));</span>
Wir können dann einen neuen Bereich für den Controller mit dem $ rootscope erstellen. $ Neue Methode. Nachdem wir eine Instanz des Controllers erstellt haben, haben wir alle Felder und Methoden in diesem neuen $ Scope.
<span>it('should show alert when title is not passed into showDialog', function(){ </span> <span>var message="Some message"; </span> sampleSvcObj<span>.showDialog(message); </span> <span>expect(mockWindow.alert).toHaveBeenCalledWith(message); </span> <span>expect(mockModalSvc.showModalDialog).not.toHaveBeenCalled(); </span><span>}); </span> <span>it('should show modal when title is passed into showDialog', function(){ </span> <span>var message="Some message"; </span> <span>var title="Some title"; </span> sampleSvcObj<span>.showDialog(message, title); </span> <span>expect(mockModalSvc.showModalDialog).toHaveBeenCalledWith({ </span> <span>message: message, </span> <span>title: title </span> <span>}); </span> <span>expect(mockWindow.alert).not.toHaveBeenCalled(); </span><span>});</span>
Da der Controller ein Feld und eine Methode zu $ Scope hinzufügt, können wir überprüfen, ob sie auf die richtigen Werte eingestellt sind und ob die Methoden die richtige Logik haben. Der obige Probencontroller fügt einen regulären Ausdruck hinzu, um eine gültige Zahl zu überprüfen. Fügen wir eine Spezifikation hinzu, um das Verhalten des regulären Ausdrucks zu testen:
angular<span>.module('controllers',[]) </span> <span>.controller('FirstController', ['$scope','dataSvc', function($scope<span>, dataSvc</span>) { </span> $scope<span>.saveData = function () { </span> dataSvc<span>.save($scope.bookDetails).then(function (result) { </span> $scope<span>.bookDetails = {}; </span> $scope<span>.bookForm.$setPristine(); </span> <span>}); </span> <span>}; </span> $scope<span>.numberPattern = <span>/<span>^\d*$</span>/</span>; </span> <span>}]);</span>
Wenn ein Controller Objekte mit Standardwerten initialisiert, können wir seine Werte in der Spezifikation überprüfen.
Um die Speichermethode zu testen, müssen wir einige Werte für die Buchdetails und Buchformobjekte festlegen. Diese Objekte wären an UI -Elemente gebunden und werden also zur Laufzeit erstellt, wenn die Ansicht kompiliert wird. Wie bereits erwähnt, müssen wir sie manuell mit einigen Werten initialisieren, bevor wir die Speichermethode aufrufen.
Die folgende Snippet testet diese Methode:
<span>module(function($provide){ </span> $provide<span>.factory('dataSvc', ['$q', function($q) </span> <span>function save(data){ </span> <span>if(passPromise){ </span> <span>return $q.when(); </span> <span>} else { </span> <span>return $q.reject(); </span> <span>} </span> <span>} </span> <span>return{ </span> <span>save: save </span> <span>}; </span> <span>}]); </span><span>});</span>
Testen eines Controllers, der den Controller als Syntax verwendet, ist einfacher als das Testen der mit $ Scope. In diesem Fall spielt eine Instanz des Controllers die Rolle eines Modells. Folglich sind alle Aktionen und Objekte in dieser Instanz verfügbar.
Betrachten Sie den folgenden Controller:
<span>beforeEach(inject(function($rootScope<span>, $controller, dataSvc</span>){ </span> scope<span>=$rootScope.$new(); </span> mockDataSvc<span>=dataSvc; </span> <span>spyOn(mockDataSvc,'save').andCallThrough(); </span> firstController <span>= $controller('FirstController', { </span> <span>$scope: scope, </span> <span>dataSvc: mockDataSvc </span> <span>}); </span><span>}));</span>
Der Prozess des Aufrufens dieses Controllers ähnelt dem zuvor diskutierten Prozess. Der einzige Unterschied besteht darin, dass wir keinen $ Scope erstellen müssen.
<span>it('should have assigned right pattern to numberPattern', function(){ </span> <span>expect(scope.numberPattern).toBeDefined(); </span> <span>expect(scope.numberPattern.test("100")).toBe(true); </span> <span>expect(scope.numberPattern.test("100aa")).toBe(false); </span><span>});</span>
Da alle Mitglieder und Methoden im Controller dieser Instanz hinzugefügt werden, können wir mit der Instanzreferenz darauf zugreifen.
Der folgende Snippet testet das zum obige Controller hinzugefügte Numberstuferfeld:
<span>it('should call save method on dataSvc on calling saveData', function(){ </span> scope<span>.bookDetails = { </span> <span>bookId: 1, </span> <span>name: "Mastering Web application development using AngularJS", </span> <span>author:"Peter and Pawel" </span> <span>}; </span> scope<span>.bookForm = { </span> <span>$setPristine: jasmine.createSpy('$setPristine') </span> <span>}; </span> passPromise <span>= true; </span> scope<span>.saveData(); </span> scope<span>.$digest(); </span> <span>expect(mockDataSvc.save).toHaveBeenCalled(); </span> <span>expect(scope.bookDetails).toEqual({}); </span> <span>expect(scope.bookForm.$setPristine).toHaveBeenCalled(); </span><span>});</span>
Behauptungen der Speichermethode bleiben gleich. Der einzige Unterschied in diesem Ansatz besteht darin, wie wir Werte mit den Buchdetails und Buchformobjekten initialisieren.
Das folgende Ausschnitt zeigt die Spezifikation:
angular<span>.module('services', []) </span> <span>.service('sampleSvc', ['$window', 'modalSvc', function($<span>window, modalSvc</span>){ </span> <span>this.showDialog = function(message<span>, title</span>){ </span> <span>if(title){ </span> modalSvc<span>.showModalDialog({ </span> <span>title: title, </span> <span>message: message </span> <span>}); </span> <span>} else { </span> $<span>window.alert(message); </span> <span>} </span> <span>}; </span> <span>}]);</span>
Anbieter werden verwendet, um eine API für die anwendungsweite Konfiguration freizulegen, die vor Beginn der Anwendung vorgenommen werden muss. Sobald die Konfigurationsphase einer AngularJS -Anwendung vorbei ist, ist die Interaktion mit Anbietern nicht zugelassen. Folglich sind Anbieter nur in Konfigurationsblöcken oder anderen Anbieterblöcken zugänglich. Wir können keine Anbieterinstanz mit einem Injektionsblock erhalten, sondern müssen einen Rückruf an den Modulblock weitergeben.
Betrachten wir den folgenden Anbieter, der von einer konstanten (AppConstants) einen zweiten Anbieter (ein anderer Antrieber) abhängt:
<span>var mockWindow, mockModalSvc, sampleSvcObj; </span><span>beforeEach(function(){ </span> <span>module(function($provide){ </span> $provide<span>.service('$window', function(){ </span> <span>this.alert= jasmine.createSpy('alert'); </span> <span>}); </span> $provide<span>.service('modalSvc', function(){ </span> <span>this.showModalDialog = jasmine.createSpy('showModalDialog'); </span> <span>}); </span> <span>}); </span> <span>module('services'); </span><span>}); </span> <span>beforeEach(inject(function($<span>window, modalSvc, sampleSvc</span>){ </span> mockWindow<span>=$window; </span> mockModalSvc<span>=modalSvc; </span> sampleSvcObj<span>=sampleSvc; </span><span>}));</span>
Um dies zu testen, müssen wir zunächst die Abhängigkeiten verspotten. Sie können sehen, wie dies im Beispielcode geht.
Vor dem Testen des Anbieters müssen wir sicherstellen, dass das Modul geladen und bereit ist. Bei Tests wird das Laden der Module aufgeschoben, bis ein Injektionsblock ausgeführt wird oder der erste Test ausgeführt wird. In einigen Projekten habe ich einige Tests gesehen, die einen leeren ersten Test zum Laden des Moduls verwenden. Ich bin kein Fan dieses Ansatzes, da der Test Ihre Gesamtzahl der Tests nicht bewertet. Stattdessen benutze ich einen leeren Injektblock, um die Module geladen zu lassen.
Das folgende Snippet erhält die Referenzen und lädt die Module:
<span>it('should show alert when title is not passed into showDialog', function(){ </span> <span>var message="Some message"; </span> sampleSvcObj<span>.showDialog(message); </span> <span>expect(mockWindow.alert).toHaveBeenCalledWith(message); </span> <span>expect(mockModalSvc.showModalDialog).not.toHaveBeenCalled(); </span><span>}); </span> <span>it('should show modal when title is passed into showDialog', function(){ </span> <span>var message="Some message"; </span> <span>var title="Some title"; </span> sampleSvcObj<span>.showDialog(message, title); </span> <span>expect(mockModalSvc.showModalDialog).toHaveBeenCalledWith({ </span> <span>message: message, </span> <span>title: title </span> <span>}); </span> <span>expect(mockWindow.alert).not.toHaveBeenCalled(); </span><span>});</span>
Jetzt, da wir alle Referenzen haben, können wir Methoden aufrufen, die in den Anbietern definiert sind und sie testen:
angular<span>.module('controllers',[]) </span> <span>.controller('FirstController', ['$scope','dataSvc', function($scope<span>, dataSvc</span>) { </span> $scope<span>.saveData = function () { </span> dataSvc<span>.save($scope.bookDetails).then(function (result) { </span> $scope<span>.bookDetails = {}; </span> $scope<span>.bookForm.$setPristine(); </span> <span>}); </span> <span>}; </span> $scope<span>.numberPattern = <span>/<span>^\d*$</span>/</span>; </span> <span>}]);</span>
Unit -Tests werden manchmal schwierig, aber es lohnt sich, die Zeit damit zu verbringen, da sie die Richtigkeit der Anwendung sicherstellt. AngularJS erleichtert die Einheitstest des Code, der mit dem Framework geschrieben wurde. Ich hoffe, dieser Artikel gibt Ihnen genügend Idee, um die Tests in Ihren Anwendungen zu erweitern und zu verbessern. In einem zukünftigen Artikel werden wir weiterhin untersuchen, wie Sie andere Teile Ihres Codes testen können.
Wie teste ich AngularJS -Dienste? und ihre Abhängigkeiten verspotten. Jasmine bietet eine Funktion namens "Spyon", mit der Sie eine Scheinfunktion erstellen und ihre Aufrufe verfolgen können. Sie können diese Funktion verwenden, um die Abhängigkeiten des Dienstes zu verspotten und sie zum Testen zu isolieren. Nach dem Einrichten des Schein des Controllers und testen seine Methoden und Eigenschaften. Sie können eine Instanz des Controllers mit dem von AngularJS bereitgestellten $ Controller -Dienst erstellen. Nach dem Erstellen der Instanz können Sie seine Methoden aufrufen und ihre Auswirkungen auf den Bereich des Controllers überprüfen. Sie können auch die Interaktion des Controllers mit Diensten testen, indem Sie die Dienste verspotten und die Aufrufe ihrer Methoden überprüfen.
Das obige ist der detaillierte Inhalt vonUnit -Tests in AngularJs: Dienste, Controller und Anbieter. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!