Node-RED node test fails when using sinon.useFakeTimer

JavaScript/TypeScript

Node-RED test fails when replacing timer related functions. Our own node derives Node-RED node and it calls setImmediate function. Therefore it doesn’t work if it is replaced with mock function by using sinon.useFakeTimer. However, sometimes it’s necessary to replace those timer friend functions. setTimeout and setInterval are not used in Node-RED node at the moment, so they can be replaced with mock function.

Sponsored links

Test fails when replacing setImmediate

Let’s test it. The tested flow looks like this below.

This is the same flow used in this post.

In the post, I didn’t replace timer friend functions but I will try to replace it this time. Firstly, let’s replace setImmediate function and see the result.

it("should succeed when replacing setTimeout and setInterval", (done) => {
    // Test fails when replacing setImmediate
    sinon.useFakeTimers({ toFake: ["setImmediate"] });
    // sinon.useFakeTimers({ toFake: ["setTimeout", "setInterval"] });

    const flow = loadFlowFile();
    helper.load(requiredNodes, flow, () => {
        const inputNode = helper.getNode(inputNodeId);
        const outputNode = helper.getNode(outputNodeId);

        outputNode.on("input", (msg: any) => {
            try {
                expect(msg.payload).to.equal("payload contains 0")
                done();
            } catch (e) {
                done(e);
            }
        });

        inputNode.wires[0].forEach((wire: string) => {
            const node = helper.getNode(wire);
            node.receive({ payload: 123450 });
        })
    });
});

Test result

$ npm run test

> node-red-docker@1.0.0 test C:\Root\Data\Programming\Typescript\BlogPost\src\node-red-docker
> mocha --recursive --require ts-node/register test/*.ts

  Node-RED flow test
    1) should succeed when replacing setTimeout and setInterval
    2) "after each" hook for "should succeed when replacing setTimeout and setInterval"


  4 passing (4s)
  2 failing

  1) Node-RED flow test
       should succeed when replacing setTimeout and setInterval:
     Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (C:\Root\Data\Programming\Typescript\BlogPost\src\node-red-docker\test\flow-test.ts)
      at listOnTimeout (internal/timers.js:554:17)
      at processTimers (internal/timers.js:497:7)

  2) Node-RED flow test
       "after each" hook for "should succeed when replacing setTimeout and setInterval":
     Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (C:\Root\Data\Programming\Typescript\BlogPost\src\node-red-docker\test\flow-test.ts)
      at listOnTimeout (internal/timers.js:554:17)
      at processTimers (internal/timers.js:497:7)

It fails because data is not forwarded to the next node. That’s why it fails due to timeout. Be careful that sinon.useFakeTimers() function replaces all timer friend functions by default.

As far as I remember, those tests that call sinon.useFakeTimers() succeeded while the Node-RED version is 0.X.X.

Test succeeds when replacing setTimeout and setInterval

Let’s keep setImmediate function but replace other timer friend functions, setTimeout and setInterval.

it("should succeed when replacing setTimeout and setInterval", (done) => {
    // Test fails when replacing setImmediate
    // sinon.useFakeTimers({ toFake: ["setImmediate"] });
    sinon.useFakeTimers({ toFake: ["setTimeout", "setInterval"] });
    ...

Test result

$ npm run test

> node-red-docker@1.0.0 test C:\Root\Data\Programming\Typescript\BlogPost\src\node-red-docker
> mocha --recursive --require ts-node/register test/*.ts

  Node-RED flow test
    √ should succeed when replacing setTimeout and setInterval

  5 passing (322ms)

This test succeeds.

Conclusion

sinon.useFakeTimers offers a way to replace only necessary function(s). Let’s replace only target function by this way below.

sinon.useFakeTimers({ toFake: ["setTimeout"] });

If there is a node that calls setTimeout or setInterval we can check whether it is called or not. We shouldn’t write a test that waits for the timer because it is not stable. Even if it works in our development environment it may not work in a build server. It depends on the spec. I suffered from it in my project. In my project, loading flow is the most time consuming part in the tests and if the tests wait for a timer it sometime fails in a server but sometimes succeeds. We should avoid such a case.

In a case that we can’t replace timer friend function we should separate a test at the problematic node. Write a test that tests the behavior from start node to the target node and from the target node to output node. We don’t have to replace the timer function in this way.

Comments

Copied title and URL