axm__
axm__

Reputation: 2673

Doing assertions inside callback of mocked readline module with jest

I'm trying to test a few things inside a callback that deals with user input. I'm using the readline module from nodejs in Jest (similar to the example given in the docs). I can't figure out how I can do assertions inside that callback.

For example: within the callback of readline.createInterface().question() I'm calling writeFileSync. I want to test how or if writeFileSync (or any other function in the callback) is called.

// file.js
const { writeFileSync } = require('fs');
const readline = require('readline');

const copyFile = () => {
    const rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout
    });

    rl.question('What is the file name?', (userInput) => {
        /**
         * I want to some assertions on this function.
         * For example: with which arguments was it called?
         * Or how often is it called?
         */
        writeFileSync();
    });
}

And in the test

//file.test.js
const { writeFileSync } = require('fs')
jest.mock('fs');

describe('copies files based on user input', () => {
    test('calls writeFileSync', () => {
        expect(writeFileSync).toHaveBeenCalled();
    });
});

The closest I got is:

// __mocks__/readline.js

module.exports = {
    createInterface: jest.fn().mockReturnValue(
        {
            question: jest.fn().mockImplementation(
                (question, cb) => {
                    writeFileSync()
                }
            )
        }
    )
}

But with that I am testing the test, I noticed. Anyone know how I can test what's inside the rl.question() callback? I'll gladly give more info if necessary!

Upvotes: 1

Views: 1374

Answers (2)

axm__
axm__

Reputation: 2673

I've found it: it needs to mock the implementation and call the callback argument (cb in this case):

// __mocks__/readline.js
module.exports = {
    createInterface: jest.fn().mockReturnValue({
        question: jest.fn()
            .mockImplementationOnce((questionText, cb) => {cb('')}) // test empty arg
            .mockImplementationOnce((questionText, cb) => {cb('foo')})
            .mockImplementationOnce((questionText, cb) => {cb('bar')})   
        })
    })
};

That returns '', foo, bar as mocked user input values.

Upvotes: 1

Mohammed Essehemy
Mohammed Essehemy

Reputation: 2176

this is called test spies and Sinon.Js has a punch of helpers, check the docs for examples

here, we can spy on fs.writeFileSync before running this test, and then use the spy inside your test.

const spy = sinon.spy(writeFileSync);

then you can assert on the spy call with spy properties such as:

 spy.callCount
 spy.calledWith()
 spy.calledOn()

Upvotes: 1

Related Questions