How do I mock an exported typescript function in a jasmine test?

Sevenless picture Sevenless · Mar 28, 2018 · Viewed 9.1k times · Source

I'm trying to mock a function exported from a typescript file in a Jasmine test. I expect the following to mock the imported foo and return the value 1 in the spec for bar.

The mock appears to be uncalled, so I'm clearly missing something. How can I fix this example?

demo.ts:

export function foo(input: any): any {
  return 2;
}

export function bar(input: any): any {
  return foo(input) + 2;
}

demo.ts.spec:

import * as demo from './demo';

describe('foo:', () => {
  it('returns 2', () => {
    const actual = demo.foo(1);
    expect(actual).toEqual(2);
  });
});

describe('bar:', () => {
  // let fooSpy;
  beforeEach(() => {
    spyOn(demo, 'foo' as any).and.returnValue(1); // 'as any' prevents compiler warning
  });

  it('verifies that foo was called', () => {
    const actual = demo.bar(1);
    expect(actual).toEqual(3); // mocked 1 + actual 2
    expect(demo.foo).toHaveBeenCalled();
  });
});

Failures:

  • Expected 4 to equal 3.
  • Expected spy foo to have been called.

Answer

Jeffrey Westerkamp picture Jeffrey Westerkamp · Mar 28, 2018

From this issue on Github: How are you expecting to use the spied on function in your actual implementation?

Your bar implementation calls the actual implementation of foo, because it has a direct reference to it. When importing in another module, a new object, with new references, is created:

// This creates a new object { foo: ..., bar: ... }
import * as demo from './demo';

These references exist only in the module of the import. When you call spyOn(demo, 'foo') it's that reference that is being used. You might want to try this in your spec, chances are the test passes:

demo.foo();
expect(demo.foo).toHaveBeenCalled();

Expecting the real implementation of bar to call a mocked foo is not really possible. Instead try to treat bar as if it had its own implementation of foo.