процесс заглушки. выход с шуткой

У меня есть код, который делает что-то вроде

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

Как я могу проверить это в Jest? Замена exit в process на jest.fn() и возврат его обратно после теста не работает, так как процесс завершается


person Nick Ginanto    schedule 11.09.2017    source источник


Ответы (6)


Другие предложения в этом потоке вызовут ошибки на моей стороне, где любые тесты с process.exit будут выполняться бесконечно. Следующий вариант работал у меня на TypeScript, но он должен работать и на JavaScript:

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

Загвоздка в том, что простое использование spyOn означало, что исходная функция process.exit() все еще вызывалась, завершая поток процесса и зависая тесты. Использование mockImplementation в конце заменяет тело функции предоставленной функцией (которая в моем примере пуста).

Этот трюк также полезен для тестов, которые выводят, скажем, на стандартный вывод. Например:

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');

Это позволит вам тестировать напечатанные значения и получить дополнительное преимущество в том, что не испортить вывод консоли CLI случайными разрывами строк.


Одно замечание: как и в случае с любым вызовом jest.spyOn, независимо от того, используется ли фиктивная реализация или нет, вам необходимо восстановить его позже, чтобы избежать странных побочных эффектов затяжных фиктивных эффектов. Таким образом, не забудьте вызвать две следующие функции в конце текущего тестового примера:

mockExit.mockRestore()
mockStdout.mockRestore()
person Epic Eric    schedule 20.07.2018
comment
mockImplementationOnce будет жаловаться в машинописном тексте, поскольку завершение процесса ожидает, что никогда не вернется, и здесь мы возвращаемся {} - person khpatel4991; 05.06.2019
comment
Одна из возможностей - это выдать ошибку в макетной реализации, которая также будет иметь тип возврата never: const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => { throw new Error('Mock'); }); Другая возможность - заставить возвращаемый тип шпиона быть недействительным, чтобы Typescript перестал жаловаться. - person Epic Eric; 05.06.2019
comment
Хороший ответ! PS: Нет необходимости передавать стрелочную функцию для mockImplementation, если вы не должны установить переменную в области тестирования и так далее. - person Lazaro Fernandes Lima Suleiman; 08.12.2019
comment
Typescript не будет жаловаться, если вы сделаете const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => { return undefined as never }); - person BigFilhao; 16.01.2020
comment
Я также нашел это полезным для имитации вызовов console.error () таким же образом! - person witacur; 23.07.2021
comment
Чтобы обойти ошибку машинописного текста, я использовал .mockImplementation((() => {}) as any). Не идеальное решение, поскольку я просто говорю Typescript игнорировать тип, но это означает, что ваше решение работает для всего, что вы хотите имитировать. - person witacur; 23.07.2021

Для большей части глобального объекта javascript я пытаюсь заменить на свою заглушку и восстановить после теста. Следующее отлично работает для меня, чтобы высмеивать 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;
    });
  });

Это помогает избежать запуска настоящего process.exit, чтобы избежать сбоев при модульном тестировании.

person joy    schedule 15.03.2018
comment
Вероятно, это какая-то странная проблема (возможно, моя) с Typescript, но я не мог заставить вышеуказанное работать - продолжал жаловаться на переназначение global.process = и фиктивный тип, не являющийся правильным типом выхода: и т. Д., А затем разбился, когда Я запустил его .. ‹shrug› - person sjmcdowall; 19.07.2018

Столкнулся с похожей проблемой. Решил это с помощью кода ниже

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')
person MWright    schedule 12.06.2018

Вы можете использовать jest.spyOn, так как это также вызовет исходный метод:

const exit = jest.spyOn(process, 'exit');
//run your test
expect(exit).toHaveBeenCalledWith('ERROR_CODE');
person Andreas Köberle    schedule 11.09.2017
comment
У меня это не сработало - шутка запускается, а затем завершает работу с вызываемым кодом выхода .. - person sjmcdowall; 19.07.2018
comment
Мне удалось заставить его работать с const exit = jest.spyOn(process, "exit").mockImplementation(number => number); - person jwineman; 28.08.2018

Перед импортом модуля у меня возникла проблема с mocking process.exit. Итак, импорт перед издевательством работал.

const { foo } = require("my-module");

const realProcessExit = process.exit;
process.exit = jest.fn(() => { throw "mockExit"; });
afterAll(() => { process.exit = realProcessExit; });

describe("foo", () => {
    it("should exit the program", () => {
        try {
            foo();
        } catch (error) {
            expect(error).toBe("mockExit");
            expect(process.exit).toBeCalledWith(1);
        }
    });
});

(важно никогда не возвращать (бросать) в mocked process.exit, чтобы foo не продолжал поток управления, как будто ничего не произошло)

person J. Doe    schedule 15.01.2021

Это сработало для меня с точки зрения шпионажа за process#exit и отсутствия ошибок типа в сигнатуре метода имитируемой реализации, которая требовала // @ts-ignore'ing:

const processExit = jest
  .spyOn(process, 'exit')
  .mockImplementation((code?: number) => undefined as never);
person DWoldrich    schedule 07.05.2021