web-dev-qa-db-fra.com

stubbing process.exit avec plaisanterie

J'ai du code qui fait quelque chose comme

 function myFunc(condition){
  if(condition){
    process.exit(ERROR_CODE)
  }
 }

Comment puis-je tester cela dans Jest? Écraser exit dans process avec jest.fn() et le renvoyer après le test ne fonctionne pas, car le processus se termine

17
Nick Ginanto

Les autres suggestions de ce fil entraîneraient des erreurs de ma part, où tous les tests avec process.exit S'exécuteraient indéfiniment. L'option suivante a fonctionné pour moi sur TypeScript, mais elle devrait également fonctionner sur JavaScript:

const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {});
myFunc(condition);
expect(mockExit).toHaveBeenCalledWith(ERROR_CODE);

Le hic est que le simple fait d'utiliser spyOn signifiait que la fonction process.exit() d'origine était toujours appelée, mettant fin au thread de processus et suspendant les tests. L'utilisation de mockImplementation à la fin remplace le corps de la fonction par la fonction fournie (qui est vide dans mon exemple).

Cette astuce est également utile pour les tests qui s'impriment sur, disons, stdout. Par exemple:

const println = (text: string) => { process.stdout.write(text + '\n'); };
const mockStdout = jest.spyOn(process.stdout, 'write').mockImplementation(() => {});
println('This is a text.');
expect(mockStdout).toHaveBeenCalledWith('This is a text.\n');

Cela vous permettra de tester les valeurs imprimées et aura l'avantage supplémentaire de ne pas gâcher la sortie de la console CLI avec des sauts de ligne aléatoires.


Une seule remarque: comme pour tout appel "jest.spyOn", que vous utilisiez ou non une implémentation fictive, vous devez la restaurer plus tard afin d'éviter les effets secondaires étranges des mockings persistants. N'oubliez donc pas d'appeler les deux fonctions suivantes à la fin du scénario de test actuel:

mockExit.mockRestore()
mockStdout.mockRestore()
25
Epic Eric

Pour la plupart de l'objet javascript global, j'essaie de le remplacer par mon stub et de le restaurer après le test. La suite fonctionne bien pour moi de se moquer de process.

  describe('myFunc', () => {
    it('should exit process on condition match', () => {
      const realProcess = process;
      const exitMock = jest.fn();

      // We assign all properties of the "real process" to
      // our "mock" process, otherwise, if "myFunc" relied
      // on any of such properties (i.e `process.env.NODE_ENV`)
      // it would crash with an error like:
      // `TypeError: Cannot read property 'NODE_ENV' of undefined`.
      global.process = { ...realProcess, exit: exitMock };

      myFunc(true);
      expect(exitMock).toHaveBeenCalledWith(ERROR_CODE);
      global.process = realProcess;
    });
  });

Cela permet d'éviter d'exécuter le vrai process.exit pour éviter de planter le test unitaire.

2
joy

Vous pouvez utiliser jest.spyOn car cela appellera également la méthode d'origine:

const exit = jest.spyOn(process, 'exit');
//run your test
expect(exit).toHaveBeenCalledWith('ERROR_CODE');
2
Andreas Köberle

J'ai fait face à un problème similaire. Résolu avec le code ci-dessous

const setProperty = (object, property, value) => {
    const originalProperty = Object.getOwnPropertyDescriptor(object, property)
    Object.defineProperty(object, property, { value })
    return originalProperty
}

const mockExit = jest.fn()
setProperty(process, 'exit', mockExit)

expect(mockExit).toHaveBeenCalledWith('ERROR_CODE')
1
MWright