With ES Modules, we define some exported functions as const
and it is often a standard practice. However, during unit testing, these const functions can't be stubbed out which seems problematic from a unit testing perspective. For example,
import * as fs from 'fs';
import { bindNodeCallback } from 'rxjs';
import { map } from 'rxjs/operators';
// readFile :: string, string => Observable<string>
export const readFile$ = bindNodeCallback(fs.readFile);
// readJson :: string => Observable<any>
export function readJson(fileName) {
return readFile$(fileName, 'utf-8')
.pipe(map((rawFile) => JSON.parse(rawFile)));
}
Now to unit test readJson, I would typically want to stub readFile$
function. Unfortunately, following Sinon code doesn't work:
// Setup data - stubs / mocks
const stub = sinon.stub(myFs, 'readFile$').returns(json$);
Since Sinon is simply changing reference myFs.readFile$
, original const
still points to the original function which is in turn called by readJson
function.
Any suggestion - how can I really stub/mock constant function within the same module?
const
is constant one can't change it using "normal" code. Unfortunately sinon
is not magic. You need to instrument your code to allow changing constant value.
Assuming you are using babel to transpile you could use babel-plugin-rewire
.
After adding it to your babel config you would be able to do the following injection in your test code
import { default as readJson, __RewireAPI__ as rewire } from './path/to/readJson.js'
const stub = sinon.stub(myFs, 'readFile$').returns(json$)
rewire.__set__('readFile$', stub)
// readJson() would now call provided stub