Angular 测试:我如何测试 angular 中包含 ajax 的承诺?

Angular test: How can I test angular promise with ajax in it?

我如何测试 angular 承诺其中包含 ajax?

代码是通过 ajax 调用父级,然后通过 ajax 调用其余的子级。

代码,

app.controller('MyController', ['$scope', '$http', '$timeout', '$q', function($scope, $http, $timeout, $q) {

    $scope.myParen = function(url) {
        var deferred = $q.defer();

        setTimeout(function() {
        $http({
            method: 'GET',
            url: url
        })
        .success(function(data, status, headers, config) {
            deferred.resolve([data]);
        })
        .error(function(data, status, headers, config) {
            deferred.reject(data);
        });
        }, 1000);

        return deferred.promise;
    }

    $scope.submit = function() {
        $scope.commentCollection = '';

        var promise = $scope.myParen('https://example.com/parents/1');

        promise.then(function(success) {
            var list = success;

            $http({
                method: 'GET',
                url: 'https://example.com/parents/1/children'
            })
            .success(function(data, status, headers, config) {
                $scope.commentCollection = list.concat(data);
            })
            .error(function(data, status, headers, config) {
                $scope.error = data;
            });
        }, function(error) {
            $scope.error = error;
        });
    };

}]);

测试,

describe('MyController Test', function() {

    beforeEach(module('RepoApp'));

    var controller, $scope, $http, $httpBackend, $q;
    var deferred;

    beforeEach(inject(function ($rootScope, $controller, $http, $httpBackend, $q) {

        $scope = $rootScope.$new();

        deferred = $q.defer();

        // Create the controller.
        controller = $controller;
        controller("MyController", {$scope, $http, $httpBackend, $q});
    }));

    it('should demonstrate using when (200 status)', inject(function($rootScope, $http, $httpBackend, $q) {

        var $scope = {};

        /* Code Under Test */
        $scope.myParen = function(url) {
            ...
        }

        $scope.submit = function() {
            ...
        };
        /* End */

        $scope.submit();

        deferred.promise.then(function (value) {

            $httpBackend.whenGET('https://example.com/parents/1/children', undefined, {})
            .respond(function(){ return [200,{foo: 'bar'}]});

            expect(value).toBe(4);
        });
        deferred.resolve(4);
        $rootScope.$apply();

        expect($scope.commentCollection).toEqual({foo: 'bar'});
    }));
});

失败的结果,

Expected '' to equal { foo: 'bar' }.

有什么想法吗?

编辑:

....
deferred.resolve(4);
$rootScope.$apply();

$timeout.flush();
expect($scope.commentCollection).toEqual({foo: 'bar'});

Re-write 您的 $scope.myParen 函数使用 $timeout 而不是 setTimeout

$scope.myParen = function(url) {

    var promise = $http({
        method: 'GET',
        url: url
    })
    .then (function(response) {
        var data = response.data;
        return $timeout(function(){return data;}, 1000);
    })
    .catch(function(response) {
        var data = response.data;
        return 
            ($timeout(angular.noop, 1000)
            ).then (function () {
                 throw data;
            });
    });

    return promise;
}

然后在测试中你可以使用$timeout.flush()同步刷新延迟函数的队列。

Deprecation Notice

The $http legacy promise methods success and error have been deprecated. Use the standard then method instead.

-- AngularJS $http Service API Reference -- deprecation notice

1) 在控制器

中将 setTimeout 切换为 $timeout

2) 替换beforeEach函数中的所有$httpBackend

3) 使用 .flush() 函数

describe('MyController Test', function() {

beforeEach(module('app'));

var controller, $scope, $http, httpBackend, $q;
var deferred;

beforeEach(inject(function ($rootScope, $controller, $http, $httpBackend, $q) {

    $httpBackend
        .whenGET('https://example.com/parents/1', undefined, {})
        .respond(function(){ return [200, {parents: []}]});

    $httpBackend
        .whenGET('https://example.com/parents/1/children', undefined, {})
        .respond(function(){ return [200, {foo: 'bar'}]});

    $scope = $rootScope.$new();

    deferred = $q.defer();

    // Create the controller.
    controller = $controller;
    controller("MyController", {$scope: $scope, $http: $http, $httpBackend: $httpBackend, $q: $q});
}));

it('should demonstrate using when (200 status)', inject(function($httpBackend, $timeout) {

    // var $scope = {}; // don't write it, becouse you rewrite a scope which defined at beforeEach function
    $scope.submit();   
    //$httpBackend.flush(); // wait when backend return parents
    $timeout.flush(); // wait timeout in $scope.myParen 
    $httpBackend.flush(); // wait when backend return children

    expect($scope.commentCollection[0]).toEqual({parents: []});
    expect($scope.commentCollection[1]).toEqual({foo: 'bar'});
}));

});