I am encountering this issue when writing unit tests for code which involves 'createReadStream' and 'readline.createInterface'.
Below is the code that I need to test:
private createReadStreamSafe(filePath: string): Promise<fs.ReadStream> {
return new Promise((resolve, reject) => {
const fileStream = fs.createReadStream(filePath)
console.log('file Stream')
fileStream
.on('error', () => {
reject('create read stream error')
})
.on('open', () => {
resolve(fileStream)
})
})
}
async start() {
const fileStream = await this.createReadStreamSafe(this.filePath)
const rl = readline.createInterface({
input: fileStream,
output: process.stdout,
terminal: false
})
for await (const line of rl) {
...
}
}
Below is my test,
it.only('should create an error if creating the read stream from the file path fails', () => {
const mockedReadStream = new Readable()
jest.spyOn(fs, 'createReadStream').mockReturnValue(mockedReadStream as any)
const app = createApp('invalid/file/path')
expect.assertions(1)
try {
app.start()
mockedReadStream.emit('error', 'Invalid file path')
} catch (error) {
expect(getErrorMessage(error)).toBe('Invalid file path')
}
})
But, I got this:
node:internal/process/promises:246
triggerUncaughtException(err, true /* fromPromise */);
^
[UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "undefined".] {
code: 'ERR_UNHANDLED_REJECTION'
}
node:internal/process/promises:246
triggerUncaughtException(err, true /* fromPromise */);
CodePudding user response:
A mock results in rejected promise which isn't handled. The test should be asynchonous and return a promise, i.e. be async
. try..catch
cannot handle it in synchronous function.
Since the promise is rejected at the time when mockedReadStream.emit is called, it needs to be chained with catch shortly after the promise is rejected, e.g. through Jest promise assertion:
let promise = app.start()
mockedReadStream.emit('error', 'Invalid file path')
await expect(promise).rejects.toThrow('Invalid file path')
This reveals the problem in tested unit because reject()
doesn't pass an error.