javascript - 创建 React App 在模拟异步函数时更改 jest.fn() 的行为

标签 javascript reactjs async-await jestjs

当从使用 npx create-react-app jest-fn-behaviour 创建的干净 CRA 项目运行时,我对 jest.fn() 的以下行为感到困惑.

例子:

describe("jest.fn behaviour", () => {
    
    const getFunc = async () => {
        return new Promise((res) => {
            setTimeout(() => {
                res("some-response");
            }, 500)
        });;
    }

    const getFuncOuterMock = jest.fn(getFunc);


    test("works fine", async () => {

        const getFuncInnerMock = jest.fn(getFunc);
        const result = await getFuncInnerMock();
        expect(result).toBe("some-response"); // passes
    })


    test("does not work", async () => {

        const result = await getFuncOuterMock();
        expect(result).toBe("some-response"); // fails - Received: undefined
    })

});

上述测试将在干净的 JavaScript 项目中按预期工作,但在 CRA 项目中不会。

有人可以解释为什么第二次测试失败吗?在我看来,在模拟异步函数时,jest.fn() 在非异步函数(例如上面的 describe)中调用时将不会按预期工作。只有在异步函数(上面的 test)中调用时,它才会起作用。但为什么 CRA 会以这种方式改变行为?

最佳答案

原因是,正如我在 another answer 中提到的那样,CRA 的默认 Jest 设置包括 the following line :

    resetMocks: true,

根据 the Jest docs ,这意味着(强调我的):

Automatically reset mock state before every test. Equivalent to calling jest.resetAllMocks() before each test. This will lead to any mocks having their fake implementations removed but does not restore their initial implementation.

正如我在评论中指出的那样,您的模拟是在测试发现 时创建的,此时 Jest 正在定位所有规范并调用 describe(但不是 it/test) 回调,而不是在执行 时调用规范回调。因此它的模拟实现毫无意义,因为它在任何测试运行之前就被清除了。

相反,您有以下三种选择:

  1. 如您所见,在测试内部创建模拟是可行的。在测试中重新配置现有的模拟也可以,例如getFuncOuterMock.mockImplementation(getFunc)(或只是getFuncOuterMock.mockResolvedValue("some-response"))。

  2. 您可以将模拟创建和/或配置移动到 beforeEach 回调中;这些在所有模拟重置后执行:

    describe("jest.fn behaviour", () => {
      let getFuncOuterMock;
      // or `const getFuncOuterMock = jest.fn();`
    
      beforeEach(() => {
        getFuncOuterMock = jest.fn(getFunc);
        // or `getFuncOuterMock.mockImplementation(getFunc);`
      });
    
      ...
    });
    
  3. resetMocks 是 CRA 的 supported keys 之一用于覆盖 Jest 配置,因此您可以添加:

      "jest": {
        "resetMocks": false
      },
    

    进入你的package.json

    但是,请注意,这可能会导致您期望(someMock).toHaveBeenCalledWith(some, args) 的误报测试,并且由于在不同的环境中与模拟交互而通过 测试。如果您要禁用自动重置,您应该更改实现以在 beforeEach 中创建模拟(即 let getFuncOuterMock;选项 2) 中的示例,以避免测试之间的状态泄漏。

请注意,这与同步与异步无关,也与模拟生命周期无关;您会在 CRA 项目(或带有 resetMocks: true Jest 配置的 vanilla JS 项目)中看到与以下示例相同的行为:

describe("the problem", () => {
  const mock = jest.fn(() => "foo");

  it("got reset before I was executed", () => {
    expect(mock()).toEqual("foo");
  });
});
  ● the problem › got reset before I was executed

    expect(received).toEqual(expected) // deep equality

    Expected: "foo"
    Received: undefined

关于javascript - 创建 React App 在模拟异步函数时更改 jest.fn() 的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66984091/

相关文章:

javascript - AngularJS 通过 localhost 向 ExpressJS 发出请求

javascript - 使用 React JS 在页面上渲染元素

javascript - 如何对数组的两种类型的值进行排序?

JavaScript/jQuery : Setting up IP Geolocation API within CodePen

css - 使用 `display: flex` 时, react 拆分 Pane 显示在其他组件上方

javascript - 在 React.js 中交换元素并获取 <error>

javascript - 从 Firebase Firestore 异步返回数据

asynchronous - 如何等待rust中的异步函数调用列表?

asynchronous - Dart 中的 await 关键字会自动处理数据依赖吗?

javascript - Redux 工具包在 React 中 createSlice