Javascript 带有$http请求的AngularJS单元测试从不触发.then()回调

Javascript 带有$http请求的AngularJS单元测试从不触发.then()回调,javascript,angularjs,json,karma-jasmine,Javascript,Angularjs,Json,Karma Jasmine,我试图在单独的json文件中使用模拟响应运行单元测试。当我使用$q返回在OpsService中手动解析的承诺时,测试是有效的,但当我试图将它们转换为实际的$http请求以返回实际的json文件时,它们不再有效 编辑:我尝试了$httpBackend.flush(),$rootScope.$apply(),和$rootScope.$digest(),但这些似乎都不能解决承诺 我的服务: OpsService.service('OpsService', function ($q, $http) {

我试图在单独的json文件中使用模拟响应运行单元测试。当我使用$q返回在OpsService中手动解析的承诺时,测试是有效的,但当我试图将它们转换为实际的$http请求以返回实际的json文件时,它们不再有效

编辑:我尝试了
$httpBackend.flush()
$rootScope.$apply()
,和
$rootScope.$digest()
,但这些似乎都不能解决承诺

我的服务:

OpsService.service('OpsService', function ($q, $http) {

    this.get = {
        bigTen : function () {
            // var defer = $q.defer();
            // defer.resolve({"data":{"alltime":125077,"record":{"date":"2016-07-19","count":825},"today":281}});
            // return defer.promise;

            return $http({
                method: 'GET',
                url: '/jsonMocks/api/big-ten.json'
            }).then(function (response) {
                console.log('bigTen data');
                console.log(response);
                return response;
            }, function (error) {
                console.log('ERROR');
                console.log(error);
            });
        },

        dashboardData : function () {
            console.log('blahhhhh');
            return $http({
                method: 'GET',
                url: '/jsonMocks/api/dashboard-data.json'
            }).then(function (response) {
                console.log('dasbhoard data');
                console.log(response);
                return response;
            }, function (error) {
                console.log('ERROR');
                console.log(error);
            });
        }
    };

    return this;
});
我的控制器:

homeModule.controller('HomeController', function ($scope, OpsService) {
    var ctrl = this;
    ctrl.loading = {
        topMetrics: true,
        dashboardData: true
    };

    function init() {
        ctrl.topMetricData();

        ctrl.getDashboardData();

        ctrl.initialized = true;
    }

    ctrl.topMetricData = function () {
        ctrl.loading.topMetrics = true;
        console.log('in topMetricData()');
        return OpsService.get.bigTen().then(function (bigTen) {
            console.log('bigTenControllerCallback');

            ctrl.loading.topMetrics = false;
            return bigTen;
        });
    };

    ctrl.getDashboardData = function () {
        ctrl.loading.dashboardData = true;
        console.log('in getDashboardData()');
        return OpsService.get.dashboardData().then(function (response) {
            console.log('getDashboardDataController Callback');

            ctrl.loading.dashboardData = false;
            return dashboardData;
        });
    };

    init();
});
我的测试:

describe('home section', function () {
    beforeEach(module('ngMockE2E'));
    beforeEach(module('templates-app'));
    beforeEach(module('templates-common'));
    beforeEach(module('LROps.home'));

    var $rootScope, $scope, $httpBackend, createController, requestHandler;

    beforeEach(inject(function($injector, _$rootScope_, _$controller_, _OpsService_) {
        $rootScope = _$rootScope_;

        $httpBackend = $injector.get('$httpBackend');

        var bigTenJson = readJSON('jsonMocks/api/big-ten.json');
        console.log(bigTenJson);
        $httpBackend.when('GET', '/jsonMocks/api/big-ten.json')
            .respond(200, { data: bigTenJson });
        // .respond(200, { data: 'test1' });

        var dashboardDataJson = readJSON('jsonMocks/api/dashboard-data.json');
        console.log(dashboardDataJson);
        $httpBackend.when('GET', '/jsonMocks/api/dashboard-data.json')
            .respond(200, { data: dashboardDataJson });
        // .respond(200, { data: 'test2' });

        var $controller = _$controller_;
        createController = function() {
            $scope = $rootScope.$new();
            return $controller('HomeController', {
                $scope : $scope,
                OpsService : _OpsService_
            });
        };
    }));

    afterEach(function() {
        $httpBackend.verifyNoOutstandingExpectation();
        $httpBackend.verifyNoOutstandingRequest();
    });

    it('should retrieve big ten data', inject(function () {
        $httpBackend.expect('GET', '/jsonMocks/api/big-ten.json');
        $httpBackend.expect('GET', '/jsonMocks/api/dashboard-data.json');

        // Controller Setup
        var ctrl = createController();

        // Initialize
        $rootScope.$apply();
        $rootScope.$digest();

        expect(ctrl.topMetrics.display.messages.count).toEqual(745);
    }));

});
因此,我的console.log()没有一个在.then()回调中触发。如果我改回返回一个
$q.defer().resolve(response).promise
对象,它似乎可以正常工作

注意:我使用karma read json来读取json文件,并在测试中做出相应的响应。据我所知,它们被正确读取,只是承诺没有得到解决,因此.then()回调可以执行。

编辑: 查看,当方法不能一起工作时,
expect
。它们是设置后端的不同选项

expect
看起来它添加了一个调用将发生的预期,并为您提供一个.respond()调用结果,以给出响应内容

when
只允许您为特定响应设置响应,而无需实际说明您期望它

因此,在您的测试中,
expect
调用会在您执行定义时覆盖
,并且不会返回任何响应,因为您没有配置响应

所以,我认为你可以摆脱期望,在你的控制器后面放一个
flush
,就像这样:

it('should retrieve big ten data', inject(function () {
    // Controller Setup
    var ctrl = createController();

    $httpBackend.flush();

    // Initialize
    $rootScope.$apply();
    $rootScope.$digest();

    expect(ctrl.topMetrics.display.messages.count).toEqual(745);
}));
或者将每个
之前的
中的时的更改为预期时的,然后您可能不需要进行刷新

编辑: 查看,当方法不能一起工作时,
expect
。它们是设置后端的不同选项

expect
看起来它添加了一个调用将发生的预期,并为您提供一个.respond()调用结果,以给出响应内容

when
只允许您为特定响应设置响应,而无需实际说明您期望它

因此,在您的测试中,
expect
调用会在您执行定义时覆盖
,并且不会返回任何响应,因为您没有配置响应

所以,我认为你可以摆脱期望,在你的控制器后面放一个
flush
,就像这样:

it('should retrieve big ten data', inject(function () {
    // Controller Setup
    var ctrl = createController();

    $httpBackend.flush();

    // Initialize
    $rootScope.$apply();
    $rootScope.$digest();

    expect(ctrl.topMetrics.display.messages.count).toEqual(745);
}));

或者将
beforeach
中的
when
s更改为
expect
s,这样您可能就不需要
flush

了。首先,每个断言的请求都应该是模拟请求。请求应该用
$httpBackend.flush()
刷新,它会触发摘要,
$rootScope.$apply()
$rootScope.$digest()
(它们彼此重复)不应该被调用

第二件事是它不应该在控制器规范中完成!控制器是一个独立的单元,依赖于一个服务,它应该与模拟服务隔离测试<代码>操作服务
是一个不同的单元

it('should retrieve big ten data', inject(function () {
    $httpBackend.expect('GET', '/jsonMocks/api/big-ten.json').respond(200, ...);
    $httpBackend.expect('GET', '/jsonMocks/api/dashboard-data.json').respond(200, ...);

    OpsService.get.bigTen().then(function (result) {
       expect(result)...
    }, function (err) {
       throw err;
    });
    OpsService.get.dashboardData()...

    $httpBackend.flush();
}));

it('should test a controller', inject(function () {
    var OpsServiceMock = { get: {
       bigTen: jasmine.createSpy().and.returnValue(...),
       dashboardData: jasmine.createSpy().and.returnValue(...)
    } };

    $scope = $rootScope.$new();

    var ctrl = $controller('HomeController', {
        $scope : $scope,
        OpsService : OpsServiceMock 
    });

    $rootScope.$digest();

    expect(OpsServiceMock.get.bigTen).toHaveBeenCalled();
    expect(OpsServiceMock.get.dashboardData).toHaveBeenCalled();
    expect...
}));

首先,每个断言的请求都应该是模拟请求。请求应该用
$httpBackend.flush()
刷新,它会触发摘要,
$rootScope.$apply()
$rootScope.$digest()
(它们彼此重复)不应该被调用

第二件事是它不应该在控制器规范中完成!控制器是一个独立的单元,依赖于一个服务,它应该与模拟服务隔离测试<代码>操作服务
是一个不同的单元

it('should retrieve big ten data', inject(function () {
    $httpBackend.expect('GET', '/jsonMocks/api/big-ten.json').respond(200, ...);
    $httpBackend.expect('GET', '/jsonMocks/api/dashboard-data.json').respond(200, ...);

    OpsService.get.bigTen().then(function (result) {
       expect(result)...
    }, function (err) {
       throw err;
    });
    OpsService.get.dashboardData()...

    $httpBackend.flush();
}));

it('should test a controller', inject(function () {
    var OpsServiceMock = { get: {
       bigTen: jasmine.createSpy().and.returnValue(...),
       dashboardData: jasmine.createSpy().and.returnValue(...)
    } };

    $scope = $rootScope.$new();

    var ctrl = $controller('HomeController', {
        $scope : $scope,
        OpsService : OpsServiceMock 
    });

    $rootScope.$digest();

    expect(OpsServiceMock.get.bigTen).toHaveBeenCalled();
    expect(OpsServiceMock.get.dashboardData).toHaveBeenCalled();
    expect...
}));

啊,是的,我也试过了,上面说没有需要刷新的调用:“错误:没有等待刷新的请求!”我会更新问题。谢谢你的快速回复!嗯,这对我来说意味着没有尝试任何请求,这很奇怪。发出请求之前的console.logs是否显示?是的,除了.then()回调函数中的日志外,我看到了每个控制台日志。我甚至在$http()请求之前有一些触发,我也看到了这些,所以我知道它进入了我的服务函数,我想我的模拟响应中出现了一些古怪的事情?所以,我注释掉了我的expect('GET')函数,它没有抱怨意外的请求。然后,我注释掉了when('GET')函数,它开始抱怨意外的响应。很确定这意味着服务正在启动$http()调用,但我的模拟响应没有返回承诺?也许我必须用承诺来包装我的json?啊,是的,我也试过了,它说没有刷新的调用:“错误:没有等待刷新的请求!”我会更新问题。谢谢你的快速回复!嗯,这对我来说意味着没有尝试任何请求,这很奇怪。发出请求之前的console.logs是否显示?是的,除了.then()回调函数中的日志外,我看到了每个控制台日志。我甚至在$http()请求之前有一些触发,我也看到了这些,所以我知道它进入了我的服务函数,我想我的模拟响应中出现了一些古怪的事情?所以,我注释掉了我的expect('GET')函数,它没有抱怨意外的请求。然后,我注释掉了when('GET')函数,它开始抱怨意外的响应。很确定这意味着服务正在启动$http()调用,但我的模拟响应没有返回承诺?也许我必须用承诺来包装我的json?是的。在我的代码的完整版本中,我是console.log(ing)从该文件读取的json,它就在那里。如果找不到文件,readJSON()函数将抛出错误。是。在我的代码的完整版本中,我是console.log(ing)从该文件中读取的json,这都是t