0

Not sure how I can mock a side-effect function with Vitest in Typescript Node.js project, Vitest 0.33.0. Consider this simplified example:

// dummy.ts
export function parent(): string {
  return `foo${child()}`;
}

export function child(): string {
  console.log('calling actual child');
  return 'bar';
}
// dummy.test.ts
describe('parent', () => {
  it('should return foobar', () => {
    const spy = vi.spyOn(dummy, 'child').mockReturnValueOnce('baz'); // also tried mockImplementationOnce
    expect(dummy.parent()).eq('foobaz'); // fails, got foobar
    expect(spy).toHaveBeenCalled(); // fails, actual child function is called
  });
});

In this simplified example, actual child() function is called instead of the spy.

The following did not work either:

vi.mock('./dummy.js', async () => {
  const actual = await vi.importActual<typeof import('./dummy.js')>('./dummy.js');
  return {
    ...actual,
    child: () => 'baz',
  };
});

describe('parent', () => {
  it('should return foobar', () => {
    expect(dummy.parent()).eq('foobaz'); // fails, got foobar
  });
});
Igor
  • 991
  • 3
  • 12
  • 27

1 Answers1

1

Internal references inside the dummy.ts module cannot mocked.

A simple alternative would be to move your child function into a different module and mock that module separately.

// dummy-parent.ts
import { child } from './dummy-child';

export function parent(): string {
  return `foo${child()}`;
}
// dummy-child.ts
export function child(): string {
  console.log('calling actual child');
  return 'bar';
}
import { dummy } from './dummy-parent';

vi.mock('./dummy-child', () => {
  return {
    child: () => 'baz',
  };
});

describe('parent', () => {
  it('should return foobar', () => {
    expect(dummy.parent()).eq('foobaz');
  });
});
Eduardo Páez Rubio
  • 1,032
  • 2
  • 9
  • 31