Sinon.js spy.called 不适用于 RPC

Sinon.js spy.called not working on RPCs

背景

我有一个使用 crossbar 注册一些 RPC 的服务器,以及一个试图确保使用 sinon 调用 RPC 的测试。

代码

server.js

"use strict";

const autobahn = require( "autobahn" );


const server = () => {

    const open = () => console.log( "Hello world" );

    const start = () => new Promise( fulfil => {

        const connection = new autobahn.Connection( {
            "url": "ws://localhost:8080/ws",
            "realm": "realm1"
        } );

        connection.onopen = session => {
            session.register( "server.open", open )
                .then(() => fulfil())
                .catch(console.log);
        };

        connection.open();
    } );

    //removing Object.freeze won't help =(
    return Object.freeze({
        start,
        open
    });
};

module.exports = server;

该服务器简单地连接到交叉开关,然后注册 open RPC。

现在是我的测试用例。我正在使用 mocha 和 chai:

test.js

"use strict";

const expect = require( "chai" )
    .expect;
const autobahn = require( "autobahn" );
const sinon = require( "sinon" );
const serverFactory = require( "./server.js" );

describe( "server", () => {

    const server = serverFactory();

    const crossbar = {
        connection: undefined,
        session: undefined
    };

    const connectToCrossbar = () => new Promise( fulfil => {

        crossbar.connection = new autobahn.Connection({
            "url": "ws://localhost:8080/ws",
            "realm": "realm1"
        });

        crossbar.connection.onopen = session => {
            crossbar.session = session;
            fulfil(); 
        };

        crossbar.connection.open();
    } );


    before( "start server", done => {
        server.start()
            .then( connectToCrossbar )
            .then( done )
            .catch( err => done( err ) );
    } );

    it( "should open", done => {
        const openSpy = sinon.spy( server, "open" );

        crossbar.session.call( "server.open", [] )
            .then( () => {
                expect( openSpy.called ).to.be.true;
                done();
            } )
            .catch( err => done( err ) );
    } );

} );

此测试也打开到 crossbar 的连接,然后调用服务器上的 open 方法。

问题

问题是即使我看到 Hello World console.log,证明该方法实际上已执行,但我的测试总是失败,因为 openSpy.called 总是 false(即使调用了 spied 方法!)。

我试过的

  1. 正在删除 Object.freeze。我知道间谍和存根实际上取代了他们正在监视的函数和对象,但在这种情况下,它没有帮助。
  2. 使用 stub 而不是 spy。当我的间谍不起作用时,我尝试用 stub 替换 open 方法并使用 callsFake 完成测试。不幸的是 callsFake 似乎从来没有被称为 ...
  3. 使用 setTimeout。我认为发生这种情况的原因可能是我即将进行测试,所以我创建了一个 setTimeout0 演变 expect 语句。也失败了。

问题

  1. 我做错了什么?
  2. 我该如何解决?

根据 testing Asynchronous code 上的 Mocha 文档:

By adding a callback (usually named done) to it(), Mocha will know that it should wait for this function to be called to complete the test.

您永远不会在 expect() 调用之后调用 done()。因此 Mocha 认为您的测试尚未完成。这应该可以解决您的问题

it( "should open", done => {
    const openSpy = sinon.spy( server, "open" );

    crossbar.session.call( "server.open", [] )
        .then( () => {
            expect( openSpy.called ).to.be.true;
            done(); // You seem to have missed this statement here
        } )
        .catch( err => done( err ) );
} );

免责声明

此解决方案基于我对 crossbar 的测试和了解。如果我错了,请随时纠正我。

我的总结

crossbar 的许多其他问题中,其中之一是无法进行任何类型的测试。 autobahn,作为 crossbar 的图书馆,也存在这些问题。

我的代码不起作用的原因是另一个进程实际上正在调用我的 open 函数而不是我的应用程序。因此,进程 运行 测试永远不知道函数被调用了。

这个问题的解决方案是让open函数return成为一个结果,然后测试我们是否收到结果:

it( "should be able to call registered RPCs", done => {
    server.getSession().call( "test1", [] )
        .then( response => {
            expect( response ).to.eql( "Hello World" );
            done();
        } )
        .catch( err => done( err ) );
} );

在现实中使用这个中间件一切都很难测试......但至少现在我有办法了!