1
votes

I've been trying to get started with unit testing in angular with karma and jasmine, and i've been pulling my hair out trying to wrap my head around how to test controllers with dependencies. I tried mocking a spy with a jasmine spyObj and registering it in the beforeEach hook, but for some reason the spy isn't being recognized.

Here's the code:

angular.module('testModule', [])
.controller('TestController', [
    '$scope',
    'TestService',
    function ($scope, TestService) {
        $scope.data = TestService.load();
    }])

.factory('TestService', function () {
    return {
        load: function(){
            return "foo";
        }
    }
});

and here's the test

describe('TestController', function() {

var $controller, $scope, TestService;

beforeEach(module('testModule'), function($provide){
    TestService = jasmine.createSpyObj("TestService", ["load"]);
    TestService.load.andReturn("bar");
    $provide.value("TestService", TestService)
});

beforeEach(inject(function(_$controller_, $rootScope, _TestService_) {
    $scope = $rootScope.$new();
    TestService = _TestService_;
    $controller = _$controller_('TestController', {
        $scope: $scope,
        TestService: TestService
    });
}));

it('should set $scope.data to bar when TestService.load is called', function() {
    expect(TestService.load).toHaveBeenCalled();
    expect($scope.data).toEqual("bar");
}); });

Both assertions in the test fail.

I get 'Error: Expected a spy, but got Function' when i call expect(TestService.load).toHaveBeenCalled();

and if I call expect($scope.data).toEqual("bar"), I get Expected 'foo' to equal 'bar'. "Foo" is coming from the actual service, not the spy object.

Thanks for your help.

2

2 Answers

1
votes

Instead of jasmine.createSpyObj, it will be easier to use the existing service that the $injector provides and then just mock the single method. You can achieve this with spyOn instead:

describe('TestController', function() {

var $controller, $scope, TestService;

beforeEach(module('testModule'));

beforeEach(inject(function(_$controller_, $rootScope, _TestService_) {
    $scope = $rootScope.$new();
    TestService = _TestService_;
    spyOn(TestService, 'load').and.returnValue('bar');
    $controller = _$controller_('TestController', {
        $scope: $scope,
        TestService: TestService
    });
}));

it('should set $scope.data to bar when TestService.load is called',       function() {
        expect(TestService.load).toHaveBeenCalled();
        expect($scope.data).toEqual("bar");
    }); 
});
1
votes

In your beforeEach you are injecting in _TestService_ and then overwriting the one you declared in the previous beforeEach via:

TestService = _TestService_;

Remove that code and your test should succeed.

Also there is no need to do this:

$provide.value("TestService", TestService)

Basically you're trying to use Angular's dependency injection when you're manually injecting things which is unnecessary.