存根过程。用开玩笑

时间:2017-09-11 04:27:02

标签: javascript mocking jestjs spy

我的代码类似于

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

如何在Jest中测试?使用exit覆盖process中的jest.fn(),并在测试后将其还原,但由于流程退出

6 个答案:

答案 0 :(得分:13)

此线程中的其他建议将导致我的错误,其中使用process.exit进行的任何测试都将无限期运行。以下选项对TypeScript适用于我,但对JavaScript也应适用:

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

要注意的是,仅使用spyOn意味着仍会调用原始的process.exit()函数,从而结束了进程线程并挂起了测试。最后使用mockImplementation会将函数主体替换为所提供的函数(在我的示例中为空)。

此技巧对于打印到例如stdout的测试也很有用。例如:

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()

答案 1 :(得分:2)

您可以使用jest.spyOn,因为这也会调用原始方法:

const exit = jest.spyOn(process, 'exit');
//run your test
expect(exit).toHaveBeenCalledWith('ERROR_CODE');

答案 2 :(得分:2)

对于大多数全局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以避免单元测试崩溃。

答案 3 :(得分:1)

我遇到了类似的问题。使用下面的代码解决了它

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

答案 4 :(得分:0)

在导入我的模块之前,我在模拟 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);
        }
    });
});

(重要的是永远不要在模拟的 process.exit 中返回(抛出),所以 foo 不会继续控制流,就好像什么都没发生一样)

答案 5 :(得分:0)

这在监视 process#exit 并且 没有得到关于需要 // @ts-ignore'ing 的模拟实现的方法签名的类型错误方面对我有用:

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