JavaScript-Jest / mock中的模拟控制台“未调用”

时间:2018-11-19 14:01:24

标签: javascript unit-testing asynchronous mocking jestjs

我正在尝试模拟console.info,我知道它将在导入的函数运行时被调用。该功能完全由单个fetch组成,当不在生产环境中运行时,它使用console.info报告请求和响应。

在问题Jest. How to mock console when it is used by a third-party-library?上,评分最高的答案建议覆盖global.console,因此我正在使用jest.spyOn进行尝试:

import * as ourModule from "../src/ourModule";

test("Thing", () => {
    // Tested function requires this. Including it here in case it's causing
    // something quirky that readers of this question may know about
    global.fetch = require("jest-fetch-mock");

    const mockInfo = jest.spyOn(global.console, "info").mockImplementation(
        () => { console.error("mockInfo") }
    );

    ourModule.functionBeingTested("test");
    expect(mockInfo).toHaveBeenCalled();
}

按预期,输出包含“ mockInfo”的实例。但是,然后使用toHaveBeenCalled()进行测试失败。

expect(jest.fn()).toHaveBeenCalled()

Expected mock function to have been called, but it was not called.

  40 |
  41 |     ourModule.functionBeingTested("test");
> 42 |     expect(mockInfo).toHaveBeenCalled();
     |                      ^
  43 | 

  at Object.toHaveBeenCalled (__tests__/basic.test.js:42:22)

console.error __tests__/basic.test.js:38
  mockInfo

我已经尝试将spyOn移至模块加载之前的位置,如答案中的注释之一所示,结果没有差异。我在这里想念什么?

这是有问题的功能:

function functionBeingTested(value) {
    const fetchData = {
        something: value
    };

    fetch("https://example.com/api", {
        method: "POST",
        mode:   "cors",
        body:   JSON.stringify(fetchData),
    })
        .then( response => {
            if (response.ok) {
                if (MODE != "production") {
                    console.info(fetchData);
                    console.info(response);
                }
            } else {
                console.error(`${response.status}: ${response.statusText}`);
            }
        })
        .catch( error => {
            console.error(error);
        });
}

1 个答案:

答案 0 :(得分:1)

问题

console.info在Promise回调中被调用,该回调在ourModule.functionBeingTested返回并运行expect时尚未执行。

解决方案

在运行console.info之前,请确保已运行调用expect的Promise回调。

最简单的方法是从Promise返回ourModule.functionBeingTested

function functionBeingTested(value) {
  const fetchData = {
    something: value
  };

  return fetch("https://example.com/api", {  // return the Promise
    method: "POST",
    mode: "cors",
    body: JSON.stringify(fetchData),
  })
    .then(response => {
      if (response.ok) {
        if (MODE != "production") {
          console.info(fetchData);
          console.info(response);
        }
      } else {
        console.error(`${response.status}: ${response.statusText}`);
      }
    })
    .catch(error => {
      console.error(error);
    });
}

...并在断言之前等待其解决:

test("Thing", async () => {  // use an async test function...
  // Tested function requires this. Including it here in case it's causing
  // something quirky that readers of this question may know about
  global.fetch = require("jest-fetch-mock");

  const mockInfo = jest.spyOn(global.console, "info").mockImplementation(
      () => { console.error("mockInfo") }
  );

  await ourModule.functionBeingTested("test");  // ...and wait for the Promise to resolve
  expect(mockInfo).toHaveBeenCalled();  // SUCCESS
});