2013-01-07 4 views
19

Ich habe ein paar Tutorials und grundlegende Beispiele durchgegangen, aber ich habe es schwer, Komponententests für meinen Controller zu schreiben. Ich habe Code-Snippets gesehen, die Controller instanziieren und das Objekt $rootScope eckig injizieren lassen, das wiederum verwendet wird, um ein neues Objekt scope für den Controller zu erstellen. Aber ich kann nicht herausfinden, warum ctrl.$scope? ist undefined:

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

    beforeEach(inject(function (_$httpBackend_, $rootScope, $controller, $filter) { 
     $httpBackend = _$httpBackend_;  

     scope = $rootScope.$new(); 
     ctrl = $controller('EmployeeCtrl', { $scope: scope}); 
     expect(ctrl).not.toBeUndefined(); 
     expect(scope).not.toBeUndefined(); //<-- PASS!  
     expect(ctrl.$scope).not.toBeUndefined(); //<-- FAIL!  
    })); 
}); 

ich statt ctrl.$scope den scope Variable landete dann aber bei meinem ersten Test konnte ich nicht herausfinden, wie Unit-Test eine Funktion Variable in meinem Controller:

Controller:

function EmployeeCtrl($scope, $http, $filter, Employee) { 
    var searchMatch = function (haystack, needle) { 
    return false; 
    } 
} 

Gebrochene Einheit Test:

it('should search ', function() {     
    expect(ctrl.searchMatch('numbers','one')).toBe(false); 
}); 

Dies ist, was ich

TypeError: Object # has no method 'searchMatch'

Wie Sie diese Funktion testen bekommen? Als Workaround habe ich meine Methode nach $ scope verschoben, so dass ich für scope.searchMatch testen konnte, aber ich fragte mich, ob das der einzige Weg ist.

Schließlich erscheint auf meinen Tests $filterist undefined zu, wie injizieren Sie es? Ich habe versucht, dies aber nicht funktioniert hat:

ctrl = $controller('EmployeeCtrl', { $scope: scope, $filter: $filter }); 

Dank

Update: Das oben genannte Verfahren $ Filter zu injizieren, funktioniert gut.

+0

mein Jasmin-Test bricht auf die inject-Methode. Es wird nicht gefunden. Was vermisse ich. eckig ist nicht null. Ich habe einen Test, der überprüft, dass er geladen ist. – Hcabnettek

Antwort

40

Mein Verständnis ist, dass in AngularJS, Sie einen Bereich Objekt an die Steuerung übergeben, wenn die Anwendung gestartet wird und der Controller den Umfang ändern. Der Controller wird nicht mehr aufgerufen.

Was ein Controller in Angularjs wirklich macht, initialisiert den Bereich: Der Controller läuft nur einmal. Wenn Sie das verstehen, Sie erkennen, dass für den Umfang, wie dies mit dem Controller zu fragen:

currentScope = myController.scope; 

ist nicht sinnvoll.

(Übrigens, eines der Dinge, die ich in Angular nicht mag, sind die Namen, die sie gewählt haben. Wenn ein 'Controller' initialisiert, dann ist es kein Controller. Es gibt eine Menge dies in der Angular API).

Ich denke, dass der ‚richtige‘ Weg, um einen ‚Controller‘ für die Prüfung in einem Jasmine before Klausel und mit Hilfe des ‚Controller‘ für Initialisierung einen neuen, leeren Rahmen, wie dies einen neuen leeren Rahmen von Grunde auf neu zu erstellen: :

var ctrl, myScope; 

beforeEach(inject(function($controller, $rootScope) { 
    myScope = $rootScope.$new(); 
    ctrl = $controller('myController', { 
     $scope: myScope 
    }); 
})); 

Und dann den neu geschaffenen Rahmen zu testen, die erwarteten Eigenschaften:

it('In the scope, the initial value for a=2', function() { 
      expect(myScope.a).toBe(2); 
}); 

Mit anderen Worten, Sie den Controller nicht testen; Sie testen den vom Controller erstellten Bereich.

Also, du machst es richtig.

+0

Das macht mehr Sinn. Danke vielmals ! – Ulises

1

Es gibt nur zwei Möglichkeiten:

hinzufügen searchMatch auf den Umfang (wie Sie bereits erwähnt) oder

Rückkehr der searchMatch Funktion:

function EmployeeCtrl($scope, $http, $filter, Employee) { 
    return { 
    searchMatch: function (haystack, needle) { 
     return false; 
    } 
    }; 
} 

Wenn es wirklich muss privat bleiben Sie dann Ich muss die Funktionen testen, die es verwenden.

Um die $filter, können Sie Folgendes versuchen:

var $filter = angular.injector(['ng']).get('$filter'); 
+0

Danke, und wie injiziere ich $ filter? – Ulises

+0

Ich habe meine Antwort aktualisiert. Versuchen Sie es mit angular.injector(), die ein $ injector-Objekt zurückgibt, das eckige Services enthält, wie $ filter, $ controller, ... – asgoth

3

Hier ist ein weiteres Muster, das einfacher sein könnte zu folgen:

var $scope; 
beforeEach(module(function ($provide) { 
    $scope = { 
    // set anything you want here or nothing 
    }; 
    $provide.value('$scope', $scope); 
})); 

var ctrl; 
beforeEach(inject(function ($controller) { 
    ctrl = $controller('MyController'); 
})); 

it('should test something...', function() { 
    // $scope will have any properties set by 
    // the controller so you can now do asserts 
    // on them. 
}); 

Sie das gleiche Muster für $ Lage und $ Fenster wie andere auch um Werte verwenden können.