Home > Net >  Jest in Node.js. How to test a function that executes shell commands
Jest in Node.js. How to test a function that executes shell commands

Time:04-27

I have not been able to test this function with Jest, I would appreciate your help. Thank you.

/**
 * @function now
 * @description When a Bash script is executed, it instantly displays the responses that appear on the screen.
 * @param {string} script Bash script
 * @example now('echo "Hello World!"')
 */
function now(script) {
  let execute = exec(script)
  execute.stdout.on('data', (data) => {
    if (data.charAt(data.length - 1) === '\n') {
      console.log(data.toString().slice(0, -1))
    } else {
      console.log(data.toString())
    }
  })
}

CodePudding user response:

Since exec executes asynchronously, you'll need to change the now function a bit in order to test it. As it's written, you don't have a way for the caller to know when now is finished. The simplest ways to change this are:

  1. Make it return a Promise that resolves when it's done
  2. Make it take a callback that is called when it's done

Promise

function now(script) {
  return new Promise((resolve, reject) => {
    let execute = exec(script)
    execute.stdout.on('data', (data) => {
      if (data.charAt(data.length - 1) === '\n') {
        console.log(data.toString().slice(0, -1))
      } else {
        console.log(data.toString())
      }
    })

    // Resolve the Promise when the shell exits
    execute.on('exit', resolve);
    // Error handling omitted for brevity, you can call reject() on failure
  });
}

Here's the test. There are various ways to check that console.log is called. This one uses a spy but you could also inject a log function and default it to console.log.

it('logs from shell command', async () => {
  jest.spyOn(console, "log");
  await greet('echo "foo"');
  expect(console.log).toBeCalledWith('foo');
});

Callback

function now(script, callback) {
  let execute = exec(script)
  execute.stdout.on('data', (data) => {
    if (data.charAt(data.length - 1) === '\n') {
      console.log(data.toString().slice(0, -1))
    } else {
      console.log(data.toString())
    }
  })

  // Resolve the Promise when the shell exits
  execute.on('exit', callback);
  // Error handling omitted for brevity, you can pass an error to the callback on failure
}

The expect needs to be in the callback so the test waits until the shell has exited. Note that you need to call done for the test to finish. Promises are generally more ergonomic, but callbacks have their uses as well so I included them both.

it('logs from shell command', (done) => {
  jest.spyOn(console, "log");
  greet('echo "foo"', () => {
    expect(console.log).toBeCalledWith('foo');
    done()
  });
});

CodePudding user response:

Using information from @helloitsjoe I got what I was looking for.

Modify the promise so that it correctly displays the messages on the screen.

 function now(script) {
  return new Promise((resolve, reject) => {
    let execute = exec(script)
    execute.stdout.on('data', (data) => {
      console.log(data.toString().slice(0, -1))
      process.stdout.cursorTo(0)
    })

    execute.on('exit', resolve);
  })

I use the test with the promise of @helloitsjoe, for it to work I need to install npm i -D regenerator-runtime and import it with import 'regenerator-runtime/runtime'.

Thank you very much @helloitsjoe.

  • Related