Heim  >  Artikel  >  Web-Frontend  >  So führen Sie einen Unit-Test in der AngularJS Library_AngularJS von JavaScript durch

So führen Sie einen Unit-Test in der AngularJS Library_AngularJS von JavaScript durch

WBOY
WBOYOriginal
2016-05-16 15:53:351166Durchsuche

Entwickler sind sich alle einig, dass Unit-Tests in Entwicklungsprojekten sehr vorteilhaft sind. Sie helfen Ihnen, die Qualität Ihres Codes sicherzustellen und sorgen so für eine stabilere Entwicklung und mehr Vertrauen, selbst wenn eine Umgestaltung erforderlich ist.

201562393334308.png (257×221)

Testgetriebenes Entwicklungsflussdiagramm

AngularJS‘ Codeanspruch einer höheren Testbarkeit ist in der Tat berechtigt. Allein die im Dokument aufgeführten End-to-End-Testbeispiele können dies veranschaulichen. Obwohl Unit-Tests bei Projekten wie AngularJS einfach sein sollen, ist es nicht einfach, sie gut durchzuführen. Obwohl die offizielle Dokumentation detaillierte Beispiele enthält, ist dies in meiner tatsächlichen Anwendung immer noch eine große Herausforderung. Hier werde ich einfach demonstrieren, wie ich es bediene.

Sofortiges Karma

Karma ist ein Testlauf-Framework, das vom Angular-Team für JavaScript entwickelt wurde. Es automatisiert problemlos Testaufgaben und ersetzt mühsame manuelle Vorgänge (z. B. Regressionstestsätze oder das Laden von Zieltestabhängigkeiten). Die Zusammenarbeit zwischen Karma und Angular ist wie Erdnussbutter und Gelee.

Sie müssen nur die Konfigurationsdatei in Karma definieren, um es zu starten, und dann führt es automatisch Testfälle in der erwarteten Testumgebung aus. Die entsprechende Testumgebung können Sie in der Konfigurationsdatei angeben. angular-seed ist eine Lösung, die ich wärmstens empfehlen kann und die schnell implementiert werden kann. Die Konfiguration von Karma in meinem letzten Projekt ist wie folgt:

module.exports = function(config) {
  config.set({
    basePath: '../',
 
    files: [
      'app/lib/angular/angular.js',
      'app/lib/angular/angular-*.js',
      'app/js/**/*.js',
      'test/lib/recaptcha/recaptcha_ajax.js',
      'test/lib/angular/angular-mocks.js',
      'test/unit/**/*.js'
    ],
 
    exclude: [
      'app/lib/angular/angular-loader.js',
      'app/lib/angular/*.min.js',
      'app/lib/angular/angular-scenario.js'
    ],
 
    autoWatch: true,
 
    frameworks: ['jasmine'],
 
    browsers: ['PhantomJS'],
 
    plugins: [
      'karma-junit-reporter',
      'karma-chrome-launcher',
      'karma-firefox-launcher',
      'karma-jasmine',
      'karma-phantomjs-launcher'
    ],
 
    junitReporter: {
      outputFile: 'test_out/unit.xml',
      suite: 'unit'
    }
 
  })
}


Dies ähnelt der Standardkonfiguration von Angular-Seed, weist jedoch die folgenden Unterschiede auf:

  • Es ist notwendig, den Browser von Chrome auf PhantomJS umzustellen, damit nicht bei jedem Sprung ein neues Browserfenster geöffnet werden muss, es kommt jedoch zu einer Fensterverzögerung im OSX-System. Daher wurden die Plugin- und Browsereinstellungen geändert.
  • Da meine Anwendung auf den Recaptcha-Dienst von Google verweisen muss, habe ich die abhängige kleine Datei recaptcha_ajax.js hinzugefügt. Diese kleine Konfiguration ist so einfach wie das Hinzufügen einer Codezeile zur Karma-Konfigurationsdatei.

autoWatch ist eine wirklich coole Einstellung, mit der Karma bei Dateiänderungen automatisch zu Ihren Testfällen zurückkehren kann. Sie können Karma wie folgt installieren:

npm install karma

Angular-Seed bietet ein einfaches Skript inscripts/test.sh zum Auslösen von Karma-Tests.


Entwerfen Sie Testfälle mit Jasmine

Die meisten Ressourcen sind bereits verfügbar, wenn Sie Unit-Testfälle für Angular mit Jasmine entwerfen – einem JavaScript-Testframework mit einem verhaltensgesteuerten Entwicklungsmodell.

Darüber möchte ich als nächstes sprechen.

Wenn Sie den AngularJS-Controller einem Unit-Test unterziehen möchten, können Sie die Abhängigkeitsinjektion von Angular verwenden. Abhängigkeitsinjektion Die Funktion importiert die vom Controller benötigte Serviceversion im Testszenario und prüft außerdem, ob die erwarteten Ergebnisse korrekt sind . Ich habe diesen Controller beispielsweise so definiert, dass er die Registerkarte hervorhebt, zu der navigiert werden muss:

app.controller('NavCtrl', function($scope, $location) {
  $scope.isActive = function(route) {
    return route === $location.path();
  };
})

Was würde ich tun, wenn ich die isActive-Methode testen möchte? Ich überprüfe, ob die Variable $locationservice den erwarteten Wert zurückgibt und ob die Methode den erwarteten Wert zurückgibt. Daher werden wir in unserer Testbeschreibung lokale Variablen definieren, um die während des Tests erforderliche kontrollierte Version zu speichern und sie bei Bedarf in den entsprechenden Controller einzuspeisen. Anschließend werden wir im eigentlichen Testfall Behauptungen hinzufügen, um zu überprüfen, ob die tatsächlichen Ergebnisse korrekt sind. Der gesamte Vorgang läuft wie folgt ab:

describe('NavCtrl', function() {
  var $scope, $location, $rootScope, createController;
 
  beforeEach(inject(function($injector) {
    $location = $injector.get('$location');
    $rootScope = $injector.get('$rootScope');
    $scope = $rootScope.$new();
 
    var $controller = $injector.get('$controller');
 
    createController = function() {
      return $controller('NavCtrl', {
        '$scope': $scope
      });
    };
  }));
 
  it('should have a method to check if the path is active', function() {
    var controller = createController();
    $location.path('/about');
    expect($location.path()).toBe('/about');
    expect($scope.isActive('/about')).toBe(true);
    expect($scope.isActive('/contact')).toBe(false);
  });
});

Mithilfe der gesamten Grundstruktur können Sie verschiedene Arten von Tests entwerfen. Da unser Testszenario die lokale Umgebung zum Aufrufen des Controllers verwendet, können Sie auch einige weitere Attribute hinzufügen und dann eine Methode zum Löschen dieser Attribute ausführen und dann überprüfen, ob die Attribute gelöscht wurden.

$httpBackendIs Cool

Was passiert also, wenn Sie $httpservice aufrufen, um Daten anzufordern oder an den Server zu senden? Glücklicherweise bietet Angular ein

Mock-Methode von $httpBackend. Auf diese Weise können Sie den Antwortinhalt des Servers anpassen oder sicherstellen, dass die Antwortergebnisse des Servers mit den Erwartungen im Komponententest übereinstimmen.

Die spezifischen Details lauten wie folgt:

describe('MainCtrl', function() {
  var $scope, $rootScope, $httpBackend, $timeout, createController;
  beforeEach(inject(function($injector) {
    $timeout = $injector.get('$timeout');
    $httpBackend = $injector.get('$httpBackend');
    $rootScope = $injector.get('$rootScope');
    $scope = $rootScope.$new();
 
 
    var $controller = $injector.get('$controller');
 
    createController = function() {
      return $controller('MainCtrl', {
        '$scope': $scope
      });
    };
  }));
 
  afterEach(function() {
    $httpBackend.verifyNoOutstandingExpectation();
    $httpBackend.verifyNoOutstandingRequest();
  });
 
  it('should run the Test to get the link data from the go backend', function() {
    var controller = createController();
    $scope.urlToScrape = 'success.com';
 
    $httpBackend.expect('GET', '/slurp?urlToScrape=http:%2F%2Fsuccess.com')
      .respond({
        "success": true,
        "links": ["http://www.google.com", "http://angularjs.org", "http://amazon.com"]
      });
 
    // have to use $apply to trigger the $digest which will
    // take care of the HTTP request
    $scope.$apply(function() {
      $scope.runTest();
    });
 
    expect($scope.parseOriginalUrlStatus).toEqual('calling');
 
    $httpBackend.flush();
 
    expect($scope.retrievedUrls).toEqual(["http://www.google.com", "http://angularjs.org", "http://amazon.com"]);
    expect($scope.parseOriginalUrlStatus).toEqual('waiting');
    expect($scope.doneScrapingOriginalUrl).toEqual(true);
  });
});

Wie Sie sehen können, ist es bei jedem Aufruf tatsächlich sehr ähnlich. Der einzige Unterschied besteht darin, dass wir $httpBackend vom Injektor erhalten, anstatt es direkt zu erhalten. Dennoch gibt es einige offensichtliche Unterschiede bei der Erstellung verschiedener Tests. Zunächst einmal wird es eine afterEachcall-Methode geben, um sicherzustellen, dass $httpBackend nach jeder Anwendungsfallausführung keine offensichtlichen abnormalen Anforderungen hat. Schaut man sich die Einstellungen des Testszenarios und die Anwendung der $httpBackend-Methode an, stellt man fest, dass es einige Dinge gibt, die nicht so intuitiv sind.

Tatsächlich ist die Methode zum Aufrufen von $httpBackend einfach und klar, reicht aber nicht aus – wir müssen den Aufruf in die Methode $scope.runTest im eigentlichen Test in der Methode zur Übergabe des Werts an $scope einkapseln .$anwenden. Auf diese Weise kann die HTTP-Anfrage erst verarbeitet werden, nachdem $digest ausgelöst wurde. Wie Sie sehen, wird $httpBackend erst analysiert, wenn wir die Methode $httpBackend.flush() aufrufen. Dadurch wird sichergestellt, dass wir während des Aufrufs überprüfen können, ob das zurückgegebene Ergebnis korrekt ist (im obigen Beispiel der $scope des Controllers). Die parseOriginalUrlStatusproperty-Eigenschaft wird an den Aufrufer übergeben, sodass wir sie in Echtzeit überwachen können)

Die nächsten Codezeilen sind Behauptungen, die das Attribut $scopethat während des Aufrufs erkennen. Cool, oder?

Tipp: Bei einigen Unit-Tests sind Benutzer daran gewöhnt, Bereiche ohne $ als Variablen zu markieren. Dies ist in der Angular-Dokumentation weder zwingend erforderlich noch wird es überbetont, aber ich verwende $scopelike, um die Lesbarkeit und Konsistenz zu verbessern.

Fazit

Vielleicht ist das eines dieser Dinge, die für mich ganz natürlich sind, aber das Schreiben von Unit-Tests in Angular zu lernen war für mich anfangs definitiv ziemlich schmerzhaft. Ich habe herausgefunden, dass ein Großteil meines Verständnisses, wie man anfängt, aus einem Flickenteppich aus verschiedenen Blogbeiträgen und Ressourcen im Internet stammte, ohne wirklich konsistente oder klare Best Practices, sondern eher durch zufällige Entscheidungen, die ganz natürlich kamen. Ich wollte eine Dokumentation dessen bereitstellen, was ich letztendlich herausgefunden habe, um anderen zu helfen, die möglicherweise Schwierigkeiten haben, weil sie einfach nur Code schreiben möchten, anstatt all die seltsamen und einzigartigen Funktionen der Angular- und Jasmine-Nutzung verstehen zu müssen. Daher hoffe ich, dass dieser Artikel Ihnen hilfreich sein kann.

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn