Search code examples
node.jstypescriptjestjsts-jest

Jest spy on promisified child process in Node.js


I have some typescript code that uses a promissified version of exec. I run it 3 times inside a method and want to spy on it to ensure the times called and that it has been called with the specific commands.

Here's what I have:

import { promisify } from 'util'
import { exec } from 'child_process';

const asyncExec = promisify(exec);

class SomeClass {
  public async someFunction() {
    await asyncExec('command 1');
    await asyncExec('command 2');
    await asyncExec('command 3');
  }
}

When I didn't have the promissified version I had this Jest test and it worked.

import { SomeClass } from './SomeClass';
import * as cp from 'child_process';

const someClass = new SomeClass();

it('should execute all 3 commands', async () => {
  const spyExec = spyOn(cp, 'exec');
  await someClass.someFunction();

  expect(spyExec).toHaveBeenCalledTimes(3);
  expect(spyExec).toHaveBeenCalledWith('command 1')
  expect(spyExec).toHaveBeenCalledWith('command 2')
  expect(spyExec).toHaveBeenCalledWith('command 3')
})

But I can't find a way to make it work now that it is a promise. Does anyone have any suggestions?


Solution

  • The const asyncExec = promisify(exec); function needs to be mocked, so instead of having it as a variable outside the class, you have to implement it as a public property in the class and then use the jest.spyOn()method to spy on it.

    In the class file it will look like this:

    import { promisify } from 'util'
    import { exec } from 'child_process';
    
    class SomeClass {
      public asyncExec = promisify(exec);
    
      public async someFunction() {
        await this.asyncExec('command 1');
        await this.asyncExec('command 2');
        await this.asyncExec('command 3');
      }
    }
    

    The test will look like this:

    import { SomeClass } from './SomeClass';
    
    const someClass = new SomeClass();
    
    it('should execute all 3 commands', async () => {
      const spyExec = spyOn(someClass , 'asyncExec');
      await someClass.someFunction();
    
      expect(spyExec).toHaveBeenCalledTimes(3);
      expect(spyExec).toHaveBeenCalledWith('command 1');
      expect(spyExec).toHaveBeenCalledWith('command 2');
      expect(spyExec).toHaveBeenCalledWith('command 3');
    })