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 => { |