Note: you can find the companion source code in bahmutov/mock-ts-imports repository.
Imagine we have the following 2 TypeScript files.
1 | export const add = (a, b) => a + b |
1 | import { add } from './math' |
Both files use named imports and exports which causes problems trying to stub them from the tests.
Testing direct named import
Let's write unit test to confirm the function add works. I will use Ava test runner. To directly load TS spec files (and source code), I will use ts-node and ava-ts.
1 | npm i -D ava ava-ts typescript ts-node |
Our first test
1 | import test from 'ava' |
It passes
1 | $ npx ava-ts math-spec.ts |
Testing transient named import
The module math.ts exports add that module user.ts calls during compute execution. Can we write a test for user.ts that stubs this indirect math.ts add export? Can we write a test where compute calls real add, and another test calls stubbed add?
1 | // "compute" imports "add" from "./math" using named import |
Yes - by using a nice utility ts-mock-imports
1 | npm i ts-mock-imports sinon |
I am installing ts-mock-imports and its peer dependency Sinon.
Let's mock named imports, even if they are loaded indirectly.
1 | // "compute" imports "add" from "./math" using named import |
Note that we had to import ./math as math object to be able to mock a named import add.
1 | npx ava-ts user-spec.ts |
Under the hood, the mockFunction uses Sinon stubs.
Restore mocks
Once mocked, the function math.add will stay mocked until restored.
1 | test('real add', t => { |
1 | npx ava-ts user-spec.ts |
I strongly recommend each test starts by restoring any mocked functions.
1 | beforeEach(ImportMock.restore) |
The third test will correctly fail, because the mock add no longer returns 100.
1 | npx ava-ts user-spec.ts |
You can also change and restore individual mock
1 | test('stub and restore', t => { |