Search code examples
javascriptangularjsjasminehttpbackendspyon

Jasmine test using spyon with $httpBackend not working


I am trying to write a jasmine test on some javascript using spyon over a method that uses $http. I have mocked this out using $httpBackend and unfortunately the spy doesn't seem to be picking up the fact the method has indeed been called post $http useage. I can see it being called in debug, so unsure why it reports it hasn't been called. I suspect I have a problem with my scope usage ? or order of $httpBackend.flush\verify ?:

Code under test

function FileUploadController($scope, $http, SharedData, uploadViewModel) {

   Removed variables for brevity
   .....

    $scope.pageLoad = function () {
        $scope.getPeriods();

        if ($scope.uploadViewModel != null && $scope.uploadViewModel.UploadId > 0) {
            $scope.rulesApplied = true;
            $scope.UploadId = $scope.uploadViewModel.UploadId;

            $scope.linkUploadedData();
        } else {
            $scope.initDataLinkages();
        }

    }


    $scope.initDataLinkages = function () {

        $http({ method: "GET", url: "/api/uploadhistory" }).
           success(function (data, status) {
               $scope.status = status;
               $scope.setUploadHistory(data);

           }).
         error(function (data, status) {
             $scope.data = data || "Request failed";
             $scope.status = status;
         });

    }

    $scope.setUploadHistory = function (data) {

        if ($scope.UploadId > 0) {
            $scope.currentUpload = data.filter(function (item) {
                return item.UploadId === $scope.UploadId;
            })[0];

            //Remove the current upload, to prevent scaling the same data!
            var filteredData = data.filter(function (item) {
                return item.UploadId !== $scope.UploadId;
            });
            var defaultOption = {
                UploadId: -1,
                Filename: 'this file',
                TableName: null,
                DateUploaded: null
            };

            $scope.UploadHistory = filteredData;

            $scope.UploadHistory.splice(0, 0, defaultOption);
            $scope.UploadHistoryId = -1;

            $scope.UploadTotal = $scope.currentUpload.TotalAmount;

        } else {
            $scope.UploadHistory = data;
        }
    }

Test setup

beforeEach(module('TDAnalytics'));
beforeEach(inject(function (_$rootScope_, $controller, _$httpBackend_) {
    $rootScope = _$rootScope_;
    $scope = $rootScope.$new();
    $httpBackend = _$httpBackend_;

    var sharedData = { currentBucket: { ID: 1 } };

    controller = $controller('FileUploadController', { $scope: $scope, SharedData: sharedData, uploadViewModel: null }); 

    $httpBackend.when('GET', '/api/Periods').respond(periods);

    $httpBackend.when('GET', '/api/uploadhistory').respond(uploadHistory);


    $scope.mappingData = {
        FieldMappings: [testDescriptionRawDataField, testSupplierRawDataField],
        UserFields: [testDescriptionUserField, testSupplierUserField]
    };
}));

afterEach(function() {
    testDescriptionRawDataField.UserFields = [];
    testSupplierRawDataField.UserFields = [];
    testTotalRawDataField.UserFields = [];

    $httpBackend.flush();
    $httpBackend.verifyNoOutstandingExpectation();
    $httpBackend.verifyNoOutstandingRequest();
});

Working test:

it('pageLoad should call linkUploadedData when user has navigated to the page via the Data Upload History and uploadViewModel.UploadId is set', function () {
    // Arrange
    spyOn($scope, 'linkUploadedData');
    $scope.uploadViewModel = {UploadId: 1};
    // Act
    $scope.pageLoad();

    // Assert
    expect($scope.rulesApplied).toEqual(true);
    expect($scope.linkUploadedData.calls.count()).toEqual(1);
});

Test that doesn't work (but should. returns count-0 but is called)

it('pageLoad should call setUploadHistory when data returned successfully', function () {
    // Arrange
    spyOn($scope, 'setUploadHistory');
    // Act
    $scope.initDataLinkages();

    // Assert
    expect($scope.setUploadHistory.calls.count()).toEqual(1);
});

Solution

  • The issue is you call httpBackend.flush() after the expect, which means success is called after you do your tests. You must flush before the expect statement.

    it('pageLoad should call setUploadHistory when data returned successfully',
    inject(function ($httpBackend, $rootScope) {
        // Arrange
        spyOn($scope, 'setUploadHistory');
        // Act
        $scope.initDataLinkages();
        $httpBackend.flush();
        $rootScope.$digest()
        // Assert
           expect($scope.setUploadHistory.calls.count()).toEqual(1);
    }));
    

    You may need to remove the flush statement from after your tests, but it probably should not be there anyway because usually it's a core part of testing behaviour and should be before expect statements.