如何使用nock记录对文件的请求和响应,并使用它在mocha验收测试中播放?

时间:2016-12-04 15:18:05

标签: javascript typescript mocha acceptance-testing nock

我继承了一个没有测试的打字稿@ 2项目。

它基本上是一个cli任务运行器,并且一个任务多次请求外部api以创建文件。作为第一个故障保护,我想设置验收测试。

因此,我想模拟对外部api的调用并从本地文件中获取响应。我如何实现这一目标?

我看了nock,因为它似乎提供了这个功能,但我该如何使用呢?

(我没有提供一个例子,因为我打算自己回答我的问题,因为我刚刚经历了整个考验。)

1 个答案:

答案 0 :(得分:3)

我已经重构了我的应用程序,当一个Task对象执行其execute方法时,所有对外部api的调用都会发生。这样的任务实现了接口ITask

import {ReadStream} from 'fs';
export interface ITask {
    execute(): Promise<ReadStream>;
}

这允许我将一个Task包装在录音机或播放装饰器中。 (我也不要让execute再创建一个文件,但它会返回一个Stream的Promise。在我的正常工作流程中,我会将该流转储到文件系统(或者将其上传到我想要的地方)

RecordDecorator:

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可能是巨大的,具体取决于流量,但这是故意的,因为我希望确保我的应用程序在其代码库的任何更改上的行为相同。 / p>