微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何使用sinon.js存根https.request response.pipe以及promise和可能的路径?

如何解决如何使用sinon.js存根https.request response.pipe以及promise和可能的路径?

我目前正在执行How to stub https.request response.pipe with sinon.js?

中所述的类似操作

实际上,我正在做同样的事情,但是我的代码有点复杂,因为我在请求代码处理方面有多种路径方式(条件),并且使用promise而不是回调。此外,我使用https而不是request模块。

我目前有以下代码无法使用:

//utils.js
/**
 * Fetches the repository archive
 *
 * @param url The archive download url
 * @param dest The temp directory path
 * @param accesstoken The access token for the repository (only available if private repository)
 */
exports.fetchArchive = function(url,dest,accesstoken) {
    let options = {
        headers: {}
    }
    if (accesstoken) {
        options.headers = {'PRIVATE-TOKEN': accesstoken}
    }
    return new Promise((resolve,reject) => {
        https
            .get(url,options,(response) => {
                const code = response.statusCode;
                if (code >= 400) {
                    reject({ code,message: response.statusMessage });
                } else if (code >= 300) {
                    this.fetchArchive(response.headers.location,dest).then(resolve,reject);
                } else {
                    response
                        .pipe(fs.createWriteStream(dest))
                        .on('end',() => resolve(null))
                        .on('error',() => reject({ code,message: response.statusMessage }));
                }
            })
    });
}

_

//utils.test.js
    describe('public fetchArchive',() => {
        it(`should have a redirect status code (>= 300) and redirect and thus be called at twice`,() => {
            let options = {
                headers: {}
            }
            options.headers = {'PRIVATE-TOKEN': repoPropsPrivate.accesstoken}

            const mockResponse = `{"data": 123}`;
            // //Using a built-in Passthrough stream to emit needed data.
            const mockStream = new Passthrough();
            mockStream.push(mockResponse);
            mockStream.end(); //Mark that we pushed all the data.

            sinon
                .stub(https,'get')
                .callsFake(function (privateUrl,callback) {
                    callback(mockStream);
                    return Promise.resolve(null); //Stub end method btw
                });


            //Finally keep track of how 'pipe' is going to be called
            sinon.spy(mockStream,'pipe');

            return utils.fetchArchive(privateArchiveUrl,privateArchiveDest,repoPropsPrivate.accesstoken)
                .then((res) => {
                    sinon.assert.calledOnce(mockStream.pipe);
                    //We can get the stream that we piped to.
                    let writable = mockStream.pipe.getCall(0).args[0];
                    assert.equal(writable.path,'./output.json');
                })
        });
    });

我不确定如何根据另一篇文章改编代码以适应我的要求。

我不知道如何发送包括请求代码和流的响应,以及如何在测试中处理承诺。

非常感谢您的帮助。

解决方法

您可以创建自定义可写流,以便对模拟数据有更多控制,例如在流对象上添加statusCodeheaders属性。我将创建类似的内容(这模拟了一个实际的流,其中_read()将从给定的responseBody发出4个字节的数据,直到没有要读取的内容为止:

const {Readable} = require('stream');

class ResponseStreamMock extends Readable {
    constructor(statusCode,responseBody) {
        super();
        this.statusCode = statusCode;
        this.headers = {location: 'someLocation'};
        this.responseData = responseBody !== undefined ? Buffer.from(JSON.stringify(responseBody),"utf8") : Buffer.from([]);
        this.bytesRead = 0;
        this.offset = 4;
    }

    _read() {
        if (this.bytesRead >= this.responseData.byteLength) {
            this.push(null);
        } else {
            setTimeout(() => {
                const buff = this.responseData.toString('utf8',this.bytesRead,this.bytesRead + this.offset);
                this.push(Buffer.from(buff));
                this.bytesRead += this.offset;
            },Math.random() * 200)
        }
    }
}

然后您可以在单元测试中像这样使用它:

describe('public fetchArchive',() => {
    it(`should redirect for status code >= 300,then pipe response into fs-stream`,async function () {
        this.timeout(5000)
        const httpGetStub = sinon.stub(https,'get');
        let redirectStream = new ResponseStreamMock(300);
        let responseStream = new ResponseStreamMock(200,{"someData": {"test": 123}});
        httpGetStub.onCall(0).callsFake(function (privateUrl,options,callback) {
            redirectStream.resume(); // immediately flush the stream as this one does not get piped
            callback(redirectStream);
        });
        httpGetStub.onCall(1).callsFake(function (privateUrl,callback) {
            callback(responseStream);
        });

        sinon.spy(redirectStream,'pipe');
        sinon.spy(responseStream,'pipe');
        const fsWriteStreamStub = sinon
            .stub(fs,'createWriteStream').callsFake(() => process.stdout); // you can replace process.stdout with new PassThrough() if you don't want any output

        const result = await fetchArchive("someURL","someDestination","")

        sinon.assert.calledOnce(fsWriteStreamStub);
        sinon.assert.calledOnce(responseStream.pipe);
        sinon.assert.notCalled(redirectStream.pipe);
        assert.equal(result,null);
    });
});

请注意,您需要稍微更改fetchArchive的实现才能使其起作用:

exports.fetchArchive = function fetchArchive(url,dest,accessToken) {
    let options = {
        headers: {}
    }
    if (accessToken) {
        options.headers = {'PRIVATE-TOKEN': accessToken}
    }
    return new Promise((resolve,reject) => {
        https
            .get(url,(response) => {
                const code = response.statusCode;
                if (code >= 400) {
                    reject({code,message: response.statusMessage});
                } else if (code >= 300) {
                    fetchArchive(response.headers.location,dest).then(resolve,reject);
                } else {
                    response.on('end',() => resolve(null))
                    response.on('error',() => reject({code,message: response.statusMessage}));
                    response.pipe(fs.createWriteStream(dest))
                }
            })
    });
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?