【问题标题】:Spying on a prototype method in Sinon.js在 Sinon.js 中监视原型方法
【发布时间】:2021-07-26 07:49:32
【问题描述】:

使用 sinon,我试图窥探原型方法。它在在线howtos 等中看起来很简单。我尝试了很多网站和这样的帖子:Stubbing a class method with Sinon.jssinon - spy on toString method,但它对我不起作用。

先决条件

我正在使用 http.d.ts https://github.com/nodejs/node/blob/master/lib/_http_outgoing.js 通过 OutgoingMessage 对象从异步 API 调用写回数据:

class OutgoingMessage extends stream.Writable

OutgoingMessage中有一个原型方法end

OutgoingMessage.prototype.end = function end(chunk, encoding, callback) {

我的 API 函数是这样调用的:

Get = async (req:IncomingMessage,res:OutgoingMessage):Promise<void> => {...
    ...
    res.end(data)
    ...
}

我的测试

在我的测试中,我调用了Get 方法。我的IncomingMessage 决定了我期望在OutgoingMessage 中的内容。

it("should call end with the correct value", async function(){ 
    ...
    let outgoingMessageSpy = sinon.spy(OutgoingMessage.prototype,"end");
    let anOutgoingMessage = <OutgoingMessage>{};
    ...
    expect(outgoingMessageSpy.calledOnce).to.be.true();
}

调试测试用例我看到end 是如何被调用的,但显然我没有以正确的方式设置我的间谍,因为我的期望失败了,calledOncefalse。检查对象我发现calledCount0

当我这样做时,我做的基本相同(在我看来)

const toStringSpy = sinon.spy(Number.prototype, "toString");
expect(toStringSpy.calledWith(2)).to.be.true;

这行得通。不过,我确实注意到,VS Code 以不同的方式突出显示关键字 prototypeNumber.prototypeOutgoingMessage.prototype。这有关系吗?鼠标悬停时显示NumberConstructor.prototype,但只有OutgoingMessage.prototype..

问题

如何正确设置间谍来接听原型方法end的调用?

【问题讨论】:

    标签: node.js mocking mocha.js prototype sinon


    【解决方案1】:

    您已正确设置间谍。 但是,有些情况会使间谍失败。例如:我看到您使用异步函数,也许您没有正确等待异步函数测试。下面的示例向您展示了这种情况。

    我有一个非常简单的 http 服务器,它的响应有延迟。

    我这样创建你的 Get 方法:

    // File: get.ts
    import { IncomingMessage, OutgoingMessage } from 'http';
    import delay from 'delay';
    
    const Get = async (req: IncomingMessage, res: OutgoingMessage): Promise<void> => {
      console.log('Get start');
      // I add this to show you that this async function need to be awaited.
      await delay(200);
      res.end(JSON.stringify({
        data: 'Hello World!'
      }));
      console.log('Get finish');
    };
    
    export default Get;
    

    然后我创建了主 index.ts 文件。

    // File: index.ts
    import http, { IncomingMessage, OutgoingMessage } from 'http';
    import Get from './get';
    
    const server = http.createServer((req: IncomingMessage, res: OutgoingMessage) => {
      console.log('Receiving IncomingMessage');
      res.setHeader('Content-Type', 'application/json');
      Get(req, res);
    });
    
    const port = 8000;
    server.listen(port);
    server.on('listening', () => {
      console.log(`Listen http on ${port}`);
    });
    

    我创建测试文件:get.spec.ts

    // File: get.spec.ts
    import sinon from 'sinon';
    import http, { OutgoingMessage } from 'http';
    import { Socket } from 'net';
    import { expect } from 'chai';
    import Get from './get';
    
    describe('GET', () => {
      it('should call end with the correct value', async () => {
        // I copy paste from your code with minor code style edit.
        const outgoingMessageSpy = sinon.spy(OutgoingMessage.prototype, 'end');
        const socket = new Socket();
        const incoming = new http.IncomingMessage(socket);
        const outgoing = new http.OutgoingMessage();
        // This is just some private property which need to be set.
        (outgoing as any)._header = true;
        // NOTE: If you want invalid response, remove the "await".
        await Get(incoming, outgoing);
    
        // Set the debug message here before expect.
        console.log('SPY Counter:', outgoingMessageSpy.callCount);
        console.log('SPY CalledOnce:', outgoingMessageSpy.calledOnce);
        expect(outgoingMessageSpy.calledOnce).to.be.true;
      });
    });
    

    当我从终端使用 ts-mocha 运行时,结果是这样的:

    $ npx ts-mocha get.spec.ts
    
    
      GET
    Get start
    Get finish
    SPY Counter: 1
    SPY CalledOnce: true
        ✓ should call end with the correct value (204ms)
    
    
      1 passing (206ms)
    

    您看到您已正确地将间谍设置为 OutgoingMessage.prototype.end。

    但是,当您在测试中删除 await 时(参见 get.spec.ts 文件中的 NOTE),结果如下:

    $ npx ts-mocha get.spec.ts
    
    
      GET
    Get start
    SPY Counter: 0
    SPY CalledOnce: false
        1) should call end with the correct value
    
    
      0 passing (8ms)
      1 failing
    
      1) GET
           should call end with the correct value:
    
          AssertionError: expected false to be true
          + expected - actual
    
          -false
          +true
    
    Get finish
    

    条件是:end 方法被正确调用,但在 it 测试被评估之后。

    【讨论】:

      猜你喜欢
      • 2013-09-17
      • 2012-07-22
      • 1970-01-01
      • 1970-01-01
      • 2013-06-20
      • 2015-05-17
      • 1970-01-01
      • 2019-11-06
      • 2018-10-25
      相关资源
      最近更新 更多