Bjarki Bjarki - 1 month ago 31
AngularJS Question

Karma testing with .success() getting 'undefined' is not an object'

I'm trying to write a unit test to see if the 'getStudents()' provider function in my controller gets called if some properties are appropriately set. Notice the .success() callback:

$scope.update = function update() {
// omitted, just doing some checking...
// finally
else if (key.length === 3 || $scope.students.length === 0) {
StudentsProvider.getStudents($scope.keyword, $scope.selectedFilters).success(function(data) {
$scope.students = data;
});
}
};


My karma unit test looks like this:

describe("Students: Controllers", function () {
var $scope;
var ctrl;
beforeEach(module('studentsApp'));

describe("SearchCtrl", function () {
// Mock the provider
var mockStudentsProvider = {
getStudents: function getStudents() {
return [
{
Education: [],
Person: [{
ID: 1,
Name: "Testing McTestsson",
SSN: "1234567890",
Address: "Fakestreet 3", MobilePhone: "7777777"
}]
}
];
}
};
var StudentsProvider;
beforeEach(inject(function ($controller, $rootScope) {
$scope = $rootScope.$new();
ctrl = $controller('SearchCtrl', { $scope: $scope, StudentsProvider: mockStudentsProvider});
StudentsProvider = mockStudentsProvider;
}));
describe("Update", function () {
beforeEach(function () {
spyOn(StudentsProvider, 'getStudents');
});
it("should always call the provider with 3 letters", function () {
$scope.keyword = "axe";
$scope.update();
expect(StudentsProvider.getStudents).toHaveBeenCalled();
expect(StudentsProvider.getStudents).toHaveBeenCalledWith("axe", "");
});
});
});
});


When I run this, I get the following error:

TypeError: 'undefined' is not an object (evaluating 'StudentsProvider.getStudents($scope.keyword, $scope.selectedFilters).success')


and it's probably because I'm not mocking the .success() callback. How would I do that? Thanks in advance!

Answer

Replace this:

var mockStudentsProvider = {

    getStudents: function getStudents() {
        return [{
            Education: [],
            Person: [{
                ID: 1,
                Name: "Testing McTestsson",
                SSN: "1234567890",
                Address: "Fakestreet 3",
                MobilePhone: "7777777"
            }]
        }];
    }
};

with this:

var mockStudentsProvider = {
    getStudents: function getStudents() {
        var retVal = [{
            Education: [],
            Person: [{
                ID: 1,
                Name: "Testing McTestsson",
                SSN: "1234567890",
                Address: "Fakestreet 3",
                MobilePhone: "7777777"
            }]
        }];
        return {
            success: function(fn) {
                fn(retVal)
            };

        }
    }
};

And replace this:

spyOn(StudentsProvider, 'getStudents');

with this:

spyOn(StudentsProvider, 'getStudents').andCallThrough();
  1. When you do not use andCallThrough() or andCallFake() jasmine prevents execution of the method and returns null. Inside your update method you are calling null.success. This will fail. (http://jasmine.github.io/1.3/introduction.html)

  2. In your mock method you need to change the return format--the real http method returns an object where success refers to a function which takes an input a callback function.

In your case, the callback function is:

function(data) {
   $scope.students = data;
}
Comments