1

I've made an extremely simple service in order to check my sanity when it comes to testing. Here it is:

(function() {
    'use strict';
    angular
        .module('app')
        .service('TestService', TestService);

    function TestService() {
        function foo() {
            bar();
        }
        function bar() {
            console.log('Hello world!');
        }
        return {
            foo: foo,
            bar: bar
        };
    }
}());

So nothing complex here - foo() calls bar() which outputs a simple 'Hello World' message to the console. My test for this looks like this:

(function() {
    'use strict';
    fdescribe('test.service',function() {
        var testService;
        beforeEach(module('app'));
        beforeEach(inject(function(_TestService_) {
            testService = _TestService_;
        })); 

        describe('setup',function() {
            it('should get the testService',function() {
                expect(testService).not.toBe(undefined);
            });
        });
        describe('foo()',function() {
            fit('should call bar',function() {
                spyOn(testService,'bar');
                testService.foo();
                expect(testService.bar).toHaveBeenCalled();
            });
        });
    });
}());

All I'm doing here is checking to see of foo calls bar in the usual method i.e. spying on bar. But this gives me the error:

Expected spy bar to have been called

I'm pulling my hair out trying to figure this one out as this cannot be something complicated - What am I fundamentally doing wrong?

Thanks!

Katana24
  • 8,706
  • 19
  • 76
  • 118
  • one thing that I might suspect that, when you user .service(), you should not return an object, otherwise use factory like angular.module('abc').factory() – Zohaib Ijaz Nov 24 '15 at 19:54
  • Why do you have underscores on either side of `_TestService_`? I've never needed to do anything like that in my tests. – HankScorpio Nov 24 '15 at 20:06
  • 1
    @HankScorpio for convenience and maintainability http://stackoverflow.com/a/15318137/1230663 – br3w5 Nov 24 '15 at 20:08
  • what @br3w5 said - it's the underscore notation that allows you to use the service name as is – Katana24 Nov 24 '15 at 20:12

1 Answers1

1

I think I see what the problem is. When you spy on testService, 'bar', jasmine will replace the 'bar' property on testService with a spy, however internally your service foo() has no reference to the testService object, so therefore it can never call that spy.

Try this:

(function() {
    'use strict';
    angular
        .module('app')
        .service('TestService', TestService);

    function TestService() {
        var service = {};
        service.foo = function() {
            service.bar();
        };
        service.bar = function() {
            console.log('Hello world!');
        };
        return service;
    }
}());
HankScorpio
  • 3,612
  • 15
  • 27
  • That did fix it but I don't really understand why there are two different references - surely spies should be referring to the function on the testService as opposed to essentially creating their own stand alone reference? – Katana24 Nov 24 '15 at 20:17
  • Consider this: create a function `function foo(){ ... }` and 2 objects that each reference it `var a = {beep: foo}; var b = {beep: foo};`. Next do this: `a.beep = someOtherFunction;` < – HankScorpio Nov 24 '15 at 20:24
  • To be clear `testService` in your test refers to the object you created on your return line `return { ... };`. It doesn't refer to the `TestService()` method. `TestService` doesn't have a `foo` or a `bar` property. – HankScorpio Nov 24 '15 at 20:27
  • 1
    Yeah that makes more sense - thanks for the help Hank and good luck with your world domination plans :) – Katana24 Nov 24 '15 at 20:34