诗乃间谍没有工作

时间:2017-05-12 14:51:54

标签: javascript node.js testing sinon

背景

我有一台小型服务器,可以从机器接收数据。每次收到消息时,我都会在调度程序对象中调用一个函数,只是console.log它收到的所有内容。

问题

代码运行良好,因为我可以在控制台中看到console.log,但Sinon spy.called不起作用。无论我拨打false多少次,它总是dispatcher.onMessage

代码

server.js

const eventDispatcher = {
    onMessage: console.log,
};

const server = (dispatcher = eventDispatcher) => {


    //this gets called everytime the server receives a message
    const onData = data => {

        //Process data
        //....
        dispatcher.onMessage(data);
    };


    const getDispatcher = () => dispatcher;

    return Object.freeze({
        getDispatcher
    });
};

test.js

describe("message sender", () => {

    const myServer = serverFactory();


    it("should send information to server", () => {
        dummyMachine.socket.write("Hello World!\r\n");

        const dataSpy = sinon.spy(myServer.getDispatcher(), "onMessage");
        expect(dataSpy.called).to.be.true; //always fails!
    });

});

研究

在阅读类似的帖子之后,我相信这是由于某些间接层发生的,如下所示:

应该使用this修复:

然而,看着我的代码,我真的无法得到我所缺少的东西。

问题

  • 我做错了什么?

MCVE

目录结构

 Project_Folder
 |____package.json
 |____server.js
 |____test
      |____ dummyMachine_spec.js

的package.json

{
  "name": "sinon-question",
  "version": "1.0.0",
  "description": "MCVE about a dummy machine connecting to a server for StackOverflow",
  "main": "server.js",
  "scripts": {
    "test": "NODE_ENV=test mocha --reporter spec --slow 5000 --timeout 5000 test/*_spec.js || true"
  },
  "author": "Pedro Miguel P. S. Martins",
  "license": "ISC",
  "devDependencies": {
    "chai": "^3.5.0",
    "mocha": "^3.3.0",
    "sinon": "^2.2.0"
  },
  "dependencies": {
    "net": "^1.0.2"
  }
}

server.js

"use strict";

const net = require("net");

const eventDispatcher = {
    onMessage: console.log,
};

const server = (dispatcher = eventDispatcher) => {

    let serverSocket;

    const onData = data => {
        //Process data
        dispatcher.onMessage(`I am server and I got ${data}`);
    };

    const start = (connectOpts) => {
        return new Promise(fulfil => {
            serverSocket = net.createConnection(connectOpts, () => {
                serverSocket.on("data", onData);   
                fulfil();
            });
        });
    };

    const stop = () => serverSocket.destroy();

    const getDispatcher = () => dispatcher;

    return Object.freeze({
        start,
        stop,
        getDispatcher
    });
};

module.exports = server;

测试/ dummyMachine.js

"use strict";


const chai = require("chai"),
    expect = chai.expect;

const sinon = require("sinon");
const net = require("net");
const serverFactory = require("../server.js");

describe("Dummy Machine", () => {

    const dummyMachine = {
        IP: "localhost",
        port: 4002,
        server: undefined,
        socket: undefined
    };

    const server = serverFactory();

    before("Sets up dummyReader and server", done => {

        dummyMachine.server = net.createServer(undefined, socket => {
            dummyMachine.socket = socket;
        });

        dummyMachine.server.listen(
            dummyMachine.port,
            dummyMachine.IP,
            undefined,
            () => {
                server.start({
                    host: "localhost",
                    port: 4002
                })
                .then(done);
            }
        );
    });

    after("Kills dummyReader and server", () => {
        server.stop();
        dummyMachine.server.close();
    });

    it("should connect to server", done => {
        dummyMachine.server.getConnections((err, count) => {
            expect(err).to.be.null;
            expect(count).to.eql(1);
            done();
        });

    });

    it("should send information to server", () => {
        dummyMachine.socket.write("Hello World\r\n");

        const dataSpy = sinon.spy(server.getDispatcher(), "onMessage");
        expect(dataSpy.called).to.be.true; //WORK DAAMN YOU!
    });
});

MCVE说明

  1. 下载文件并创建指示的目录结构。
  2. 输入项目文件夹并在终端上输入npm install
  3. 输入npm test
  4. 第一个测试应该通过,这意味着实际上正在进行连接。

    即使您获得了控制台日志,第二次测试也会失败,证明onMessage已被调用。

2 个答案:

答案 0 :(得分:1)

主要问题是仅仅监视onMessage是不够的,因为你的测试在完全被调用时永远不会找到(因为流事件是异步的)递送)。

您可以使用setTimeout()进行黑客攻击,并在向服务器发送消息后检查并查看它是否被调用,但这并不理想。

相反,您可以将onMessage替换为将被调用的函数,并且从该函数中,您可以测试并查看是否使用正确的参数调用它等。

Sinon提供stubs可用于此目的:

it("should send information to server", done => {
  const stub = sinon.stub(server.getDispatcher(), 'onMessage').callsFake(data => {
    stub.restore();
    expect(data).to.equal('I am server and I got Hello World\r\n');
    done();
  });
  dummyMachine.socket.write("Hello World\r\n");
});

而不是原始onMessage,它将调用"假函数"你提供的。在那里,存根被恢复(这意味着onMessage恢复到其原始状态),您可以检查并查看是否使用正确的参数调用它。

由于测试是异步的,因此使用done

有几件事需要考虑:

  • 由于编程错误,onMessage无法在所有中调用,因此您无法轻松检测到。当发生这种情况时,几秒钟后,Mocha将超时测试,导致测试失败。
  • 如果发生这种情况,存根将无法恢复到原始状态,并且任何尝试存根onMessage的后续测试都将失败,因为该方法已经存根(通常,您可以通过创建onBeforeEach中的存根,并将其恢复为onAfterEach
  • 此解决方案不会测试onMessage的内部工作原理,因为它已被替换。{li>它只测试它是否被调用,并且使用正确的参数(但是,它更好,更容易,单独测试onMessage,通过直接使用测试用例中的各种参数调用它。)

答案 1 :(得分:0)

我猜这个问题是由你想要侦察的对象使用Object.freeze造成的。

大部分时间这些"间谍"技术通过用另一个实现" spying"的函数覆盖sped on function来工作。功能(例如:跟踪函数调用)。

但是,如果您要冻结对象,则无法覆盖该功能。