#node.js #typescript #unit-testing #jestjs #nestjs
#node.js #машинопись #модульное тестирование #jestjs #nestjs
Вопрос:
Я использую Nestjs и написал функцию ниже, которая получает файл из post-запроса и сохраняет его в папке в моем проекте. Моя проблема в том, что я не уверен, как протестировать on('error')
ветку.
функция для модульного тестирования.
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', () => {
console.log(createReadStream);
reject(false);
})
);
});
}
Как я тестирую on('finish')
ветку
it('should save file', async () => {
const returnedFile = await service.saveFile(mockFile);
expect(returnedFile).toBe(true);
});
Вот как выглядит мой макет файла. Я попытался предоставить макет файла с пустым именем, но он выдал ошибку.
export const mockFile: FileUpload = {
filename: 'zoro',
mimetype: 'image/jpeg',
encoding: '7bit',
createReadStream(): ReadStream {
return fs.createReadStream(join(process.cwd(), `apps/mull-api/uploads/mock-upload/zoro`));
},
};
Комментарии:
1. Вы ищете модульное тестирование или интеграционное тестирование?
2. почему вы используете асинхронную функцию внутри promise?
3. @slideshowp2 Я пытаюсь выполнить модульное тестирование этой функции. Мне нужно достичь порога 80% покрытия в моем проекте
4. @SyedMisharNewaz Я следовал указаниям гида, и вот как это было сделано там stephen-knutter.github.io/2020-02-07-nestjs-graphql-file-upload
Ответ №1:
Мы можем издеваться createWriteStream
.on('finish')
и .on('error')
использовать методы mockImplementation()
. И запускаем эти два события в функции макетной реализации самостоятельно.
'finish'
Событие handler
в функции макетной реализации является () => resolve(true)
; 'error'
Событие handler
в функции макетной реализации является () => reject(false);
См. Макет реализации и пример ниже:
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));
});
});
результат модульного теста:
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
Комментарии:
1. Эй, извините, но я не понимаю в вашем тестовом примере на ошибку, что на самом деле вызывает
error
событие?2. @AriaGroult обновил ответ с дополнительными объяснениями.