如何测试Jest中的console.log输出

122

我正在使用 create-react-app 并尝试编写一个 jest 测试,以检查 console.log 的输出。

我的测试函数如下:

export const log = logMsg => console.log(logMsg);

我的测试是:

it('console.log the text "hello"', () => {
  console.log = jest.fn('hello');
  expect(logMsg).toBe('hello');
});

这是我的错误信息

 FAIL  src/utils/general.test.js
  ● console.log the text hello

    expect(received).toBe(expected)    Expected value to be (using ===):      "hello"
    Received:
      undefined
    Difference:
      Comparing two different types of values. Expected string but received undefined.

你的函数在测试中被调用在哪里?在进行断言之前,你应该先调用它。 - JeB
抱歉,我不明白你的意思。 - Hello-World
请问您能否包含整个测试文件?您在哪里声明了 logMsg - ltamajs
这就是我所拥有的,logMsg 是指传递的文本。 - Hello-World
4个回答

134

如果你想检查 console.log 接收到了正确的参数(即你传入的参数),你应该检查你的 jest.fn()mock
此外,你还需要调用你的 log 函数,否则 console.log 将不会被调用:

it('console.log the text "hello"', () => {
  console.log = jest.fn();
  log('hello');
  // The first argument of the first call to the function was 'hello'
  expect(console.log.mock.calls[0][0]).toBe('hello');
});
或者
it('console.log the text "hello"', () => {
  console.log = jest.fn();
  log('hello');
  // The first argument of the first call to the function was 'hello'
  expect(console.log).toHaveBeenCalledWith('hello');
});

如果您采用这种方法,请不要忘记恢复 console.log 的原始值。

另一个选择是使用jest.spyOn(而不是替换console.log,它将创建一个代理):

it('console.log the text "hello"', () => {
  const logSpy = jest.spyOn(console, 'log');

  console.log('hello');

  expect(logSpy).toHaveBeenCalledWith('hello');
});

点击这里阅读更多内容。


失败 src/utils/general.test.js ● 控制台输出文本“你好”类型错误:specificMockImpl.apply不是函数,在CustomConsole.mockConstructor [as log] (node_modules/jest-mock/build/index.js:288:37)中 在Object..exports.logger.logMsg (src/utils/general.js:13:51) 在Object..it (src/utils/general.test.js:16:23) 在新的Promise () 在Promise.resolve.then.el (node_modules/p-map/index.js:46:16) 在 - Hello-World
我的错误。jest.fn('hello') 应该是 jest.fn()。现在再试一下。另外,如果它回答了你的问题,你可以接受它作为正确答案。 - JeB
感谢您的回答。在前两个示例中,您写了 log('hello')。您是不是想写成 console.log('hello') - Mir-Ismaili
1
@Mir-Ismaili 不,我实际上是指 loglog 是你的函数,它调用 console.log。你想检查它是否正常工作,因此要使用模拟替换 console.log。然后调用你的 log 函数(它应该调用 console.log),并确保模拟确实被调用了。 - JeB

86

或者你可以这样做:

it('calls console.log with "hello"', () => {
  const consoleSpy = jest.spyOn(console, 'log');

  console.log('hello');

  expect(consoleSpy).toHaveBeenCalledWith('hello');
});

3
这是最安全、副作用最小的答案,我建议它胜过其他解决方案。你也可以使用spy来静音默认行为,jest会确保在测试结束时正确地恢复一切(不像其他大多数答案)。这也是最简洁和组合的方法。 - ProLoser
2
为了让其他人更清楚 @ProLoser 所暗示的意思,你可以使用 jest.spyOn(console, 'log').mockImplementation(() => {}) 来防止日志混乱你的测试日志。 - blwinters

18

另一种选择是保存对原始日志的引用,在每个测试中使用jest mock替换它,并在所有测试完成后恢复原始日志。这有一个轻微的好处,即不会污染测试输出,仍然可以为调试目的使用原始日志方法。

describe("Some behavior that will log", () => {
  const log = console.log; // save original console.log function
  beforeEach(() => {
    console.log = jest.fn(); // create a new mock function for each test
  });
  afterAll(() => {
    console.log = log; // restore original console.log after all tests
  });
  test("no log", () => {
    // TODO: test something that should not log
    expect(console.log).not.toHaveBeenCalled();
  });
  test("some log", () => {
    // TODO: execute something that should log
    expect(console.log).toHaveBeenCalledWith(
      expect.stringContaining("something")
    );
    const message = console.log.mock.calls[0][0]; // get actual log message
    log(message); // actually log out what the mock was called with
  });
});


3
唯一可在隔离测试中正常运行的解决方案。这应该被接受作为答案,因为其他解决方案对已经被记录的事情会给出错误的负面响应。 - Coco Liliace
我尝试了这个帖子中大多数其他评分较高的答案,但只有这个有效,而且最易读且没有副作用。 - undefined

3

这个问题只出现在特定的测试中吗? - Colin D
2
你可以调用mockReset()mockClear()来重置console.log的模拟。 - Simply007
嗯,我只是担心这样的语句会产生全局副作用。我想在我知道要记录日志的测试中仅模拟控制台。再加上测试是并行运行的事实?如果它产生全局副作用,那么重置日志似乎是不够的,因为测试是并行运行的。 - Colin D

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接