使用$ http请求的AngularJS单元测试永远不会触发.then()回调

时间:2016-08-18 14:35:28

标签: javascript angularjs json karma-jasmine

我试图在单独的json文件中使用模拟响应运行单元测试。当我使用$ q返回在我的OpsService中手动解析的promises时,测试工作正常,但是当我尝试将它们转换为实际的$ 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()回调可以执行。

2 个答案:

答案 0 :(得分:4)

首先,每个声明的请求都应该被模拟请求。请求应使用$httpBackend.flush()刷新,它会触发摘要,$rootScope.$apply()$rootScope.$digest()(它们相互重复)不应该被调用。

第二件事是它不应该在控制器规范中完成!控制器是一个依赖于服务的独立单元,它应该与模拟服务隔离进行测试。 OpsService是一个不同的单位。

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...
}));

答案 1 :(得分:0)

编辑: 查看documentation for $httpBackendexpectwhen方法无法协同工作。他们设置后端的选项不同。

expect看起来会增加对呼叫发生的期望,并为您提供一个.respond(),您可以对结果进行调用以提供响应内容。

when只是让您为特定回复设置回复,而不会实际说出您的期望。

因此,在您的测试中,expect来电会覆盖您所做的when定义,并且不会返回任何回复,因为您没有对其进行配置。

所以,我认为你可以摆脱期望并在你的控制器之后放一个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);
}));

或者将when中的beforeEach更改为expect,然后您可能不会需要flush