You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在JavaScript中Mock含Promise的函数?解决loginCognito只读报错

问题:测试时修改loginCognito报错"loginCognito is read-only"

我有一个AWS Cognito登录的辅助函数(路径:src/utils/calls/aws.js):

export const loginCognito = (cognitoUser, authenticationDetails) => {
  cognitoUser.authenticateUser(authenticationDetails, {
    onSuccess: function (result) {
      return result;
    },
    onFailure: function (error) {
      return error;
    },
  });
};

在测试文件中尝试mock这个函数时,报错"loginCognito is read-only",测试代码如下:

import { loginCognito } from "../../../utils";

describe("Test LoginPage", () => {

  it("Can press button without userEvent", async () => {

    loginCognito = jest.fn().mockResolvedValue({accessToken: "a2b3"});

    renderWithProviders(<LoginPage />);

    const inputUsername = screen.getByLabelText(/username/i);
    fireEvent.change(inputUsername, {
      target: {
        value: "info@example.com",
      },
    });

    const inputPassword = screen.getByLabelText(/password/i);
    fireEvent.change(inputPassword, {
      target: {
        value: "thisIsASecretPassword",
      },
    });

    const loginButton = screen.getByRole("button", { name: /login/i });
    fireEvent.click(loginButton);
    await waitFor(() => {
      expect(localStorage.getItem(LOCALSTORAGE_ACCESS_TOKEN)).toEqual(
        accessToken
      );
    });
  });
});

我需要跳过loginCognito的真实实现,直接返回指定值,求解决方案。


解决方案1:使用Jest模块Mock(无需修改源文件)

ES模块导出的是只读引用,直接赋值会报错,需要用Jest的jest.mock替换整个模块的导出:

修改测试文件的导入和Mock部分:

// 先Mock整个utils模块,保留其他未Mock的函数,仅替换loginCognito
jest.mock('../../../utils', () => ({
  ...jest.requireActual('../../../utils'),
  loginCognito: jest.fn().mockResolvedValue({accessToken: "a2b3"})
}));

// 导入Mock后的函数
import { loginCognito } from "../../../utils";

describe("Test LoginPage", () => {
  it("Can press button without userEvent", async () => {
    // 可根据测试需求临时调整返回值,比如用mockResolvedValueOnce
    // loginCognito.mockResolvedValueOnce({accessToken: "test-token"});

    renderWithProviders(<LoginPage />);

    // 以下测试逻辑保持不变
    const inputUsername = screen.getByLabelText(/username/i);
    fireEvent.change(inputUsername, {
      target: {
        value: "info@example.com",
      },
    });

    const inputPassword = screen.getByLabelText(/password/i);
    fireEvent.change(inputPassword, {
      target: {
        value: "thisIsASecretPassword",
      },
    });

    const loginButton = screen.getByRole("button", { name: /login/i });
    fireEvent.click(loginButton);
    await waitFor(() => {
      // 注意期望值要和Mock返回的accessToken一致
      expect(localStorage.getItem(LOCALSTORAGE_ACCESS_TOKEN)).toEqual("a2b3");
    });
  });
});

解决方案2:调整源文件导出结构(允许修改源文件时使用)

把函数包装在对象中导出,这样可以修改对象属性实现Mock:

修改src/utils/calls/aws.js:

export const authUtils = {
  loginCognito: (cognitoUser, authenticationDetails) => {
    cognitoUser.authenticateUser(authenticationDetails, {
      onSuccess: function (result) {
        return result;
      },
      onFailure: function (error) {
        return error;
      },
    });
  }
};

测试文件中修改导入和Mock:

import { authUtils } from "../../../utils";

describe("Test LoginPage", () => {
  it("Can press button without userEvent", async () => {
    // 直接Mock对象中的方法
    authUtils.loginCognito = jest.fn().mockResolvedValue({accessToken: "a2b3"});

    renderWithProviders(<LoginPage />);

    // 以下测试逻辑保持不变
  });
});

内容的提问来源于stack exchange,提问作者sineverba

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 22:31:11