2014-02-22 4 views
8

Mein CompanyService ist:Wie verspotten Sie das Ergebnis eines Versprechens in einem AngularJS-Komponententest?

angular.module('mean').service('CompanyService', ['$http', '$rootScope', '$q', function($http, $rootScope, $q) { 
    var company = this; 
    var initializedDeferred = $q.defer(); 

    company.company_data = {} 
    company.initialized = initializedDeferred.promise; 

    company.getCompany = function() { 
    return company.company_data; 
    } 

    company.get = function (company_id) { 
    return $http({ 
     url: '/api/v1/company/' + company_id, 
     method: 'GET', 
     headers: { 
     api_key: $rootScope.api_key 
     } 
    }).success(function(response) { 
     if(response.status === 'ok') { 
     company.company_data = response.company; 
     initializedDeferred.resolve(); 
     } else { 
     alert('TBD: Need fail case'); 
     } 
    }); 
    }; 
}]); 

Mein Controller ist:

angular.module('mean').controller('LocationController', ['$scope', '$location', '$rootScope', 'LocationService', 'UserService', 'CompanyService', '$modal', '$routeParams', function ($scope, $location, $rootScope, LocationService, UserService, CompanyService, $modal, $routeParams) { 
    $rootScope.menuItem = 'locations'; 
    $scope.contentTemplate = '/views/location/index.html'; 
    $scope.locations = []; 
    $scope.current_location = null; 
    $scope.newLocation = {}; 
    $scope.location_parent_id = $routeParams.location_parent_id; 

    $scope.index = function() { 
    CompanyService.initialized.then(function() { 
     $scope.test = 'a'; 
     var company_id = CompanyService.getCompany()._id; 
     LocationService.list(company_id, $routeParams.location_parent_id).then(function(response) { 
     if(response.data.status === 'ok') { 
      $scope.locations = response.data.locations; 
      $scope.current_location = response.data.location || null; 
     } 
     }); 
    }); 
    } 

    $scope.addLocationModal = function() { 
    $scope.location_types = ['warehouse', 'section', 'row', 'shelf', 'bin']; 
    $modal({ 
     scope: $scope, 
     template: '/views/location/addLocationModal.html', 
     show: true, 
     animation: 'am-fade-and-scale' 
    }); 
    } 

    $scope.createLocation = function() { 
    $scope.newLocation.company_id = CompanyService.getCompany()._id; 
    LocationService.create($scope.newLocation).then(function(response) { 
     if(response.data.status === 'ok') { 
     $scope.$hide(); 
     } else { 
     alert('TBD'); 
     } 
    }); 
    } 

}]); 

Mein Test ist:

(function() { 
    describe('LocationController', function() { 
    var $scope, $location, $rootScope, $modal, deferred, CompanyService, createController; 

    beforeEach(module('mean')); 

    beforeEach(inject(function($injector) { 
     $location = $injector.get('$location'); 
     $rootScope = $injector.get('$rootScope'); 
     $modal = $injector.get('$modal'); 
     $scope = $rootScope.$new(); 

     var $controller = $injector.get('$controller'); 

     var $q = $injector.get('$q'); 

     var params = { 
     '$scope': $scope, 
     CompanyService: jasmine.createSpyObj('CompanyService', ['initialized']) 
     } 

     params.CompanyService.initialized.andCallFake(function() { 
     deferred = $q.defer(); 
     return deferred.promise; 
     }); 


     createController = function() { 
     return $controller('LocationController', params); 
     }; 
    })); 

    it('should instantiate initial variables at the top level', function() { 
     var controller = createController(); 

     $location.path('/company/locations'); 
     expect($location.path()).toBe('/company/locations'); 
     expect($rootScope.menuItem).toBe('locations'); 
     expect($scope.contentTemplate).toBe('/views/location/index.html'); 
     expect($scope.locations.length).toEqual(0); 
     expect($scope.current_location).toBeNull(); 
     expect($scope.newLocation).toBeDefined(); 
     expect($scope.location_parent_id).not.toBeDefined(); 
    }); 

    it('should list all locations for this company', function() { 
     var controller = createController(); 
     deferred.resolve(); 
     $scope.index(); 

     expect($scope.test).toBe('a'); 
    }); 
    }); 
})(); 

Aber die resolve nicht funktioniert. Ich erhalte diesen Fehler: TypeError: 'undefined' is not an object (evaluating 'deferred.resolve')

Irgendwelche Hilfe?

+0

In Ihrem letzten Test in der 'Test' Teil des Codes haben Sie 'es sollte alle Standorte für diese Firma auflisten', Sie haben eine aufgeschobene dort nie erstellt und versuchen, ca Ich löse es auf, es ist undefiniert. (Welches ist genau das, was der Fehler dir gesagt hat). –

+1

versuchen Sie es mit 'sinon.js', es ist ein fantastisches Spott Framework. – Gyandeep

+0

@BenjaminGruenbaum - Ich bin mir nicht ganz sicher, was du meinst. Ist das nicht "aufgeschoben" im Rahmen der gesamten Suite, weil ich "var deferent" im obersten Bereich bin? – Shamoon

Antwort

6

In Ihren Tests erstellen Sie Ihr verzögertes Objekt mit der falschen CompanyService.initialized-Funktion. Diese Funktion wird jedoch nur aufgerufen, wenn Sie $scope.index(); aufrufen, was nach Ihrer deferred.resolve(); Zeile ausgeführt wird. Folgendes sollte funktionieren:

it('should list all locations for this company', function() { 
    var controller = createController(); 
    $scope.index(); // Should in turn call the fake CompanyService.initialized function that creates deferred 
    deferred.resolve(); 
    $scope.$apply(); // Fire $digest cycle to dispatch promises. 

    expect($scope.test).toBe('a'); 
}); 

aktualisieren

Jasmin nicht Ausspionieren von Eigenschaften unterstützen, die keine Funktionen sind. Ihre Spy-Einrichtung ist also ungültig, da CompanyService.initialized ein Objekt und keine Funktion ist, so dass Ihre andCallFake nicht funktioniert. Eine Abhilfe ist eine Getter-Funktion in Ihrem CompanyService zB vorstellen:

company.isInitialized(){ return company.initialized; } 

Und dann in Ihrer Controller Verwendung dieser Getter-Funktion statt:

$scope.index = function() { 
    CompanyService.isInitialized().then(function() { 
    $scope.test = 'a'; 
    // Removed for brevity 
    }); 
} 

Und schließlich Ihren Testcode Initialisierung aktualisiert:

var params = { 
    '$scope': $scope, 
    CompanyService: jasmine.createSpyObj('CompanyService', ['isInitialized']) 
} 
params.CompanyService.isInitialized.andCallFake(function() { 
    deferred = $q.defer(); 
    return deferred.promise; 
}); 
+1

'TypeError: 'undefined' ist keine Funktion (Bewertung von 'CompanyService.initialized.then')' – Shamoon

+0

@Shamoon Jasmine unterstützt das Ausspionieren von Eigenschaften, die keine Funktionen sind, nicht. Daher ist Ihre Spy-Konfiguration ungültig, da "CompanyService.initialized" ein Objekt und keine Funktion ist, so dass Ihr 'andCallFake' nicht funktioniert. Siehe aktualisierte Antwort mit einer Problemumgehung. – Beyers

+0

Jetzt ist dies fehlgeschlagen: 'LocationController sollte initiale Variablen auf der obersten Ebene instanziieren FAILED \t Erwartete undefined als 'Standorte'. \t Erwartet undefined zu sein '/ views/location/index.html'.' – Shamoon

Verwandte Themen