We can mock createWriteStream, .on('finish') and .on('error') methods using mockImplementation(). And trigger these two events in the mock implementation function by ourself.
The 'finish' event handler in the mock implementation function is () => resolve(true); The 'error' event handler in the mock implementation function is () => reject(false);
See Mock Implementations, and below example:
const myMockFn = jest.fn(cb => cb(null, true));
myMockFn((err, val) => console.log(val));
// > true
index.ts:
import { createWriteStream, ReadStream } from 'fs';
import { join } from 'path';
export interface FileUpload {
filename: string;
mimetype: string;
encoding: string;
createReadStream(): ReadStream;
}
export class FileService {
public saveFile({ createReadStream, filename }: FileUpload): Promise<boolean> {
return new Promise(async (resolve, reject) => {
createReadStream().pipe(
createWriteStream(join(process.cwd(), `apps/mull-api/uploads/${filename}`))
.on('finish', () => resolve(true))
.on('error', () => {
reject(false);
}),
);
});
}
}
index.test.ts:
import { FileService, FileUpload } from './';
import { createWriteStream, WriteStream } from 'fs';
import { mocked } from 'ts-jest/utils';
jest.mock('fs');
describe('64485251', () => {
afterAll(() => {
jest.resetAllMocks();
jest.clearAllMocks();
});
it('should save file', async () => {
const mockReadStream = { pipe: jest.fn() };
const mockFile: FileUpload = {
filename: 'zoro',
mimetype: 'image/jpeg',
encoding: '7bit',
createReadStream: jest.fn().mockReturnValueOnce(mockReadStream),
};
const mockWriteStream = {
on: jest.fn().mockImplementation(function(this, event, handler) {
if (event === 'finish') {
handler();
}
return this;
}),
};
mocked(createWriteStream).mockReturnValueOnce((mockWriteStream as unknown) as WriteStream);
const service = new FileService();
const actual = await service.saveFile(mockFile);
expect(mockFile.createReadStream).toBeCalledTimes(1);
expect(mockReadStream.pipe).toBeCalledTimes(1);
expect(createWriteStream).toBeCalledWith(expect.stringContaining('apps/mull-api/uploads/zoro'));
expect(mockWriteStream.on).toBeCalledWith('finish', expect.any(Function));
expect(mockWriteStream.on).toBeCalledWith('error', expect.any(Function));
expect(actual).toBeTruthy();
});
it('should handle error if save file failed', async () => {
const mockReadStream = { pipe: jest.fn() };
const mockFile: FileUpload = {
filename: 'zoro',
mimetype: 'image/jpeg',
encoding: '7bit',
createReadStream: jest.fn().mockReturnValueOnce(mockReadStream),
};
const mockWriteStream = {
on: jest.fn().mockImplementation(function(this, event, handler) {
if (event === 'error') {
handler();
}
return this;
}),
};
mocked(createWriteStream).mockReturnValueOnce((mockWriteStream as unknown) as WriteStream);
const service = new FileService();
await expect(service.saveFile(mockFile)).rejects.toEqual(false);
expect(mockFile.createReadStream).toBeCalledTimes(1);
expect(mockReadStream.pipe).toBeCalledTimes(1);
expect(createWriteStream).toBeCalledWith(expect.stringContaining('apps/mull-api/uploads/zoro'));
expect(mockWriteStream.on).toBeCalledWith('finish', expect.any(Function));
expect(mockWriteStream.on).toBeCalledWith('error', expect.any(Function));
});
});
unit test result:
PASS src/stackoverflow/64485251/index.test.ts (10.201s)
64485251
✓ should save file (6ms)
✓ should handle error if save file failed (3ms)
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 11.344s