Я реорганизовал свое приложение, чтобы все вызовы внешнего API происходили, когда объект Task выполняет свой метод execute
. Такая задача реализует интерфейс ITask
:
import {ReadStream} from 'fs';
export interface ITask {
execute(): Promise<ReadStream>;
}
Это позволило мне обернуть задачу либо внутри записывающего устройства, либо в декораторе воспроизведения. (Я также больше не позволяю execute
создавать файл, но он возвращает Promise of a Stream. В моем обычном рабочем процессе я бы выгружал этот поток в файловую систему (или загружал его куда угодно).
Рекорддекоратор:
import {writeFile} from 'fs';
import {ITask} from './ITask';
import nock = require('nock');
import mkdirp = require('mkdirp');
import {ReadStream} from 'fs';
export class TaskMockRecorder implements ITask {
constructor(private task: ITask, private pathToFile: string) {
}
public async execute(): Promise <ReadStream> {
this.setupNock();
const stream = await this.task.execute();
this.writeRecordFile();
return Promise.resolve(stream);
}
private writeRecordFile() {
const nockCallObjects = nock.recorder.play();
mkdirp(this.pathToFile, async() => {
writeFile(`${this.pathToFile}`, JSON.stringify(nockCallObjects, null, 4));
});
}
private setupNock() {
nock.recorder.rec({
dont_print: true,
enable_reqheaders_recording: true,
output_objects: true,
});
}
}
PlayBackDecorator
import {ITask} from './ITask';
import {ReadStream} from 'fs';
import {Partner} from '../Types';
import nock = require('nock');
export class TaskMockPlaybackDecorator implements ITask {
constructor(private task: ITask, private pathToFile: string) {
}
public async execute(): Promise<ReadStream> {
nock.load(this.pathToFile);
nock.recorder.play();
return this.task.execute();
}
}
Оформление задачи
Кроме того, я представил пользовательский тип MockMode
:
export type MockeMode = 'recording'|'playback'|'none';
который я затем могу внедрить в свою функцию appRunner:
export async function appRun(config: IConfig, mockMode: MockeMode): Promise<ReadStream> {
let task: ITask;
task = new MyTask(config);
const pathToFile = `tapes/${config.taskName}/tape.json`;
switch (mockMode) {
case 'playback':
console.warn('playback mode!');
task = new TaskMockPlaybackDecorator(task, path);
break;
case 'recording':
console.warn('recording mode!');
task = new TaskMockRecorder(task, path);
break;
default:
console.log('normal mode');
}
const csvStream = await task.execute();
return Promise.resolve(csvStream);
}
Проведение приемочного испытания:
Теперь мне нужно было добавить эталонные файлы и настроить тест mocha, который сравнивает сгенерированный поток из запуска воспроизведения с эталонным файлом:
import nock = require('nock');
import {appRun} from '../../src/core/task/taskRunner';
import {createReadStream} from 'fs';
import {brands} from '../../src/config/BrandConfig';
import isEqual = require('lodash/isEqual');
const streamEqual = require('stream-equal');
describe('myTask', () => {
const myConfig = { // myConfig // }
const referencePath = `references/${myConfig.taskName}.csv`;
it(`generates csv that matches ${referencePath}`, async() => {
nock.load(`tapes/${config}.taskName}/tape.json`);
nock.recorder.play();
return new Promise(async(resolve, reject) => {
const actual = await appRun(myConfig, 'playback');
const expected = createReadStream(referencePath);
streamEqual(actual, expected, (err: any, isEqual: boolean) => {
if (err) {
reject(err);
}
if (isEqual) {
resolve('equals');
return;
}
reject('not equals');
});
});
});
});
В зависимости от размера записанного на пленку запроса/ответа json может потребоваться увеличить размер запуска с помощью timeout
, так как по умолчанию это 2 секунды, и такие тесты могут выполняться медленнее.
mocha --recursive dist/tests -t 10000
Этот подход также позволяет легко обновлять ленты, можно просто передать параметр mockMode
в качестве аргумента, и он обновит файл tape.json
.
Недостатком является то, что tape.json
может быть огромным в зависимости от объема трафика, но это было преднамеренно, поскольку в качестве первого шага я хотел убедиться, что мое приложение ведет себя одинаково при любых изменениях в его кодовой базе.
person
k0pernikus
schedule
04.12.2016