1
votes

I'm trying to spy on methods defined in a controller, but no matter what I do I see test failures with the message:

Error: Expected a spy, but got Function.

I'm using Karma, Jasmine and Sinon alongside Angular. I'm pretty sure things are set up correctly because tests that just read properties from the $scope pass.

For example, I have this very simple app module:

angular.module('app', []);

And this very simple controller:

angular.module('app').controller('myController', ['$scope', function($scope) {
    $scope.test = '';
    $scope.setTest = function (newString) {
        $scope.test = newString || 'default';
    }
    $scope.updateTest = function (newString) {
        $scope.setTest(newString);
    };
}]);

My spec file is as follows:

describe('myController', function () {
    'use strict';

    beforeEach(module('app'));

    var $scope, sandbox;

    beforeEach(inject(function ($controller) {
        $scope = {};
        $controller('myController', { $scope: $scope });

        sandbox = sinon.sandbox.create();
    }));

    afterEach(function () {
        sandbox.restore();
    });

    describe('#updateTest()', function () {

        beforeEach(function () {
            sandbox.spy($scope, 'setTest');
        });

        it('updates the test property with a default value', function () {
            $scope.updateTest();

            expect($scope.test).toEqual('default');
        });

        it('calls the setTest method', function () {
            $scope.updateTest();

            expect($scope.setTest).toHaveBeenCalled();

        });

    });
});

The first test (where it's just checking the test property gets updated) passes.

The second test, where I just want to spy on the setTest() method, fails with the error message above.

If I log out the $scope in the beforeEach I can see the setTest method and there are no script errors.

What am I missing?

1

1 Answers

2
votes

I guess it's happening because you are mixing Jasmine and Sinon, I do no think that Sinon spy sandbox.spy() gonna work with Jasmine matcher expect().toHaveBeenCalled(). You should choose which one to use:

  1. Use Sinon spies and convert the result to primitive to pass it to Jasmine:

    sandbox.spy($scope, 'setTest');
    expect($scope.setTest.called).toBeTruthy();
    

    But this approach will give you less verbose output: Expected true to be false, instead of usual Expected spy to have been called.

  2. Use Jasmine spies:

    spyOn($scope, 'setTest');
    expect($scope.setTest).toHaveBeenCalled();
    

Also you can take a look at the tool jasmine-sinon, which adds extra Jasmine matchers and allows to use Sinon spies with Jasmine spy matchers. As a result you should be able to use like in your sample:

sandbox.spy($scope, 'setTest');
expect($scope.setTest).toHaveBeenCalled();