Javascript Angularjs单元测试解决方案

Javascript Angularjs单元测试解决方案,javascript,angularjs,unit-testing,jasmine,spy,Javascript,Angularjs,Unit Testing,Jasmine,Spy,我可能做错了,但我不知道如何修复它 我想测试一个使用资源(ngResource)的控制器,我想使用间谍作为该资源的双重测试,这样它就不会真正执行http调用。在下面的代码中,我只想测试控制器中的搜索功能 控制器: controllers = angular.module('app.controllers'); controllers.controller('landingCtrl', ['$scope', '$q', 'categoryResource', function ($scope, $

我可能做错了,但我不知道如何修复它

我想测试一个使用资源(ngResource)的控制器,我想使用间谍作为该资源的双重测试,这样它就不会真正执行http调用。在下面的代码中,我只想测试控制器中的搜索功能

控制器:

controllers = angular.module('app.controllers');
controllers.controller('landingCtrl', ['$scope', '$q', 'categoryResource', function ($scope, $q, categoryResource) {

    $scope.search = function (text) {
        console.log('searching for: ' + text);
        var deferred = $q.defer();
        categoryResource.query({ searchTerm: text }, function (result) {
            if (result.length == 0) {
                deferred.resolve(['No results found']);
            } else {
                deferred.resolve(result);
            }
        });
        return deferred.promise;
    }
}]);
服务:

var services = angular.module('app.services');    
services.factory('categoryResource', ['$resource', function ($resource) {
    var resource = $resource('/api/category');    
    return resource;
}]);
着陆控制规范:

describe('Controller: landingCtrl ', function () {

    var $q,
        $rootScope,
        $scope;

    beforeEach(module('ngResource'));
    beforeEach(module('app.services'));
    beforeEach(module('app.controllers'));

    beforeEach(inject(function (_$rootScope_, _$q_) {
        $q = _$q_;
        $rootScope = _$rootScope_;
    }));

    // mock any depencencies, like scope. $resource or $http
    beforeEach(inject(function ($controller, $injector, categoryResource) {
        $scope = $rootScope.$new();

        spyOn(categoryResource, 'query').andCallFake(function (searchText) {
            console.log('query fake being called');
            var deferred = $q.defer();
            deferred.resolve(['Test', 'Testing', 'Protester']);
            return deferred.promise;
        });

        landingCtrl = $controller('landingCtrl', {
            '$scope': $scope,
            '$q': $q,
            'categoryResource': categoryResource
        });
    }));

    afterEach(inject(function ($rootScope) {
        $rootScope.$apply();
    }));

    it('should return words with "test" in them"', function () {
        $scope.search('test').then(function (results) {
            console.log(results);
            expect(results).toContain('Test');
        });
        $scope.$apply();
    });
});
测试执行时没有错误,但它通过时没有解析承诺,因此“then”函数中的代码永远不会被调用。我做错了什么

我已经用上述方法创建了一个plunker,并且测试应该失败:


您的规范正在模拟
categoryResource.query()
,因此它会返回一个承诺,但您的控制器并不期望这样。它调用
query()
并传递回调,然后在该回调中执行它的操作。换句话说,您的规范没有测试控制器的功能

这是您的固定规格:

spyOn(categoryResource, 'query').andCallFake(function (searchText, callback) {
    console.log('query fake being called');
    callback(['Test', 'Testing', 'Protester']);
});

...

it('should return words with "test" in them"', function () {
    var results;

    $scope.search('test').then(function (_results_) {
        console.log(results);
        results = _results_;
    });
    $scope.$apply();

    expect(results).toContain('Test');
});


请注意,我已将预期移到
then()
回调之外,因此,如果承诺未得到解决,您的测试将失败。

谢谢您的正确答案,Michael!这是可行的,但我不完全明白为什么。这是因为landingCtrl.search()返回承诺,categoryResource.query的签名是(object,callback)?@VictorH.Ponce
categoryResource.query()
方法不返回承诺;它接收回调并在稍后调用它。您的规范在模仿它,好像它返回了一个承诺,因此您的控制器没有正确执行(事实上,
query()
回调根本没有执行)。