React:如何在使用Jest进行测试时模拟Auth0?

11

我正在使用React (react-create-app和TypeScript)。登录功能是通过Auth0实现的。

我想用Jest编写测试,并找到了这个资源,它基本上是唯一一个涉及模拟Auth0对象的内容。

因此,我的代码看起来像这样:

import React from "react";
import ReactDOM from "react-dom";
import TopBar from "./index";
import {
  useAuth0
} from "react-auth0-spa";

const user = {
  email: "johndoe@me.com",
  email_verified: true,
  sub: "google-oauth2|12345678901234"
};

// intercept the useAuth0 function and mock it
jest.mock("react-auth0-spa");

describe("First test", () => {
  beforeEach(() => {
    // Mock the Auth0 hook and make it return a logged in state
    useAuth0.mockReturnValue({
      isAuthenticated: true,
      user,
      logout: jest.fn(),
      loginWithRedirect: jest.fn()
    });
  });

  it("renders without crashing", () => {
    const div = document.createElement("div");
    ReactDOM.render( < TopBar / > , div);
  });
});

但是我一直卡在这个错误上:

“Property 'mockReturnValue' does not exist on type '() => IAuth0Context | undefined'.ts(2339)”

我有点迷失了,希望能得到任何帮助!

3个回答

11

我用了大约一个小时的时间自己解决了这个问题。问题源于修改模拟返回值后应用到useAuth0时应用了不正确的类型。我的解决方案是使用'ts-jest/utils'中的mocked函数。您也可以向用户对象添加角色、范围等信息(请参见adminUser对象)。

更新2022-11-21:随着版本28.0+的发布,ts-jest包中的mocked函数已被移除(请参见mockedv27文档)。

此函数现已弃用,并将在28.0.0中删除。该函数已作为Jest 27.4.0的一部分集成到jest-mock包中,请使用jest-mock中的函数(请参见https://github.com/facebook/jest/pull/12089)。

import { render, screen } from "@testing-library/react";
import { useAuth0 } from "@auth0/auth0-react";

// if you're using jest 27.4.0+ and ts-jest 28.0.0+
import { mocked } from "jest-mock";

// if you're using ts-jest < 28.0.0
// import { mocked } from "ts-jest/utils";

const user = {
   email: "johndoe@me.com",
   email_verified: true,
   sub: "google-oauth2|12345678901234",
};

const adminUser = {
   email: "johndoe@me.com",
   email_verified: true,
   sub: "google-oauth2|12345678901234",
   "https://<<API_URL>>/roles": ["admin", "superuser"],
};


jest.mock("@auth0/auth0-react");
 
const mockedUseAuth0 = mocked(useAuth0, true);
 
describe("TopNav Component Tests - Logged in", () => {
   beforeEach(() => {
       mockedUseAuth0.mockReturnValue({
           isAuthenticated: true,
           user,
           logout: jest.fn(),
           loginWithRedirect: jest.fn(),
           getAccessTokenWithPopup: jest.fn(),
           getAccessTokenSilently: jest.fn(),
           getIdTokenClaims: jest.fn(),
           loginWithPopup: jest.fn(),
           isLoading: false,
       });
   });
   test("Logout Button displays when logged in", () => {
       render(
               <TopNav />
       );
       const loginButton = screen.getByText(/Logout/i);
       expect(loginButton).toBeInTheDocument();
   });
   test("Make sure Admin Panel Button doesnt show without Role", () => {
       render(
               <TopNav />
       );
       const adminPanelButton = screen.queryByText(/Admin Panel/i);
       expect(adminPanelButton).toBeNull();
   });
});

describe("TopNav Component Tests - Admin User", () => {
   beforeEach(() => {
       mockedUseAuth0.mockReturnValue({
           isAuthenticated: true,
           user: adminUser,
           logout: jest.fn(),
           loginWithRedirect: jest.fn(),
           getAccessTokenWithPopup: jest.fn(),
           getAccessTokenSilently: jest.fn(),
           getIdTokenClaims: jest.fn(),
           loginWithPopup: jest.fn(),
           isLoading: false,
       });
   });
   test("Admin Panel Button displays", () => {
       render(
               <TopNav />
       );
       const adminPanelButton = screen.getByText(/Admin Panel/i);
       expect(adminPanelButton).toBeInTheDocument();
   });
});

7

这是一个TypeScript错误。您需要将模拟的useAuth0类型化,因为原始类型没有名为mockReturnValue的方法。像这样的东西应该可以工作:

const mockedUseAuth0 = <jest.Mock<typeof useAuth0>>useAuth0;

mockedUseAuth0.mockReturnValue({
  isAuthenticated: true,
  user,
  logout: jest.fn(),
  loginWithRedirect: jest.fn()
});

5
阅读了本帖子、遇到相似的错误并仔细研究了Jest文档后,我成功模拟了useAuth0()钩子,同时使用了正常的Auth0导入和标准的Jest模拟功能。
如果这个解决方案有改进或存在问题,请告诉我,因为我是新手在使用Jest。
我所做的如下所述。在此示例中,我使用FooBar作为React组件,FooBar.test.js作为对应的Jest测试脚本。 步骤1:在您的FooBar.test.js中,创建一个变量来表示已登录/已注销。
// flag to switch between logged-in and logged-out, default to logged-out
let mockLoginStatus = false;

步骤 2: 在您的 FooBar.test.js 文件中,创建一个用户,为 loggedin 和 loggeout 设置值,并创建一个模拟 useAuth0 函数的函数

// mock user info
const auth0User = {
  nickname: 'marydoe',
  name: 'marydoe@gmail.com',
  email: 'marydoe@gmail.com',
};

// value returned for useAuth0 when logged-in
const mockUseAuth0LoggedIn = {
  isAuthenticated: true,
  isLoading: false,
  user: auth0User,
  logout: jest.fn(),
  loginWithRedirect: jest.fn()
};

// value returned for useAuth0 when logged-out
const mockUseAuth0LoggedOut = {
  isAuthenticated: false,
  isLoading: false,
  user: null,
  logout: jest.fn(),
  loginWithRedirect: jest.fn()
};

// mock useAuth0 function that return logged-in or logged-out values
const mockUseAuth0 = (status) => {
  if (status) {
    return mockUseAuth0LoggedIn;
  }
  return mockUseAuth0LoggedOut;
};

步骤三:在你的 FooBar.test.js 文件中,设置你对 @auth0 库的模拟。只模拟需要模拟的函数,同时保留其他功能与原始模块相同。

jest.mock('@auth0/auth0-react', () => {
  // load original module
  const originalModule = jest.requireActual('@auth0/auth0-react');

  // mock only the functions we want to mock
  return {
    __esModule: true,
    ...originalModule,
    useAuth0: jest.fn(() => mockUseAuth0(mockLoginStatus))
  };
});

第四步:在你的FooBar.test.js文件中,设置beforeAll和afterAll钩子函数,以便按照你的意愿进行登录(或不登录)。

describe('FooBar component, authenticated', () => {
  
  // login, authenticate
  beforeAll(() => {
    mockLoginStatus = true;
  });
  // logout
  afterAll(() => {
    mockLoginStatus = false;
  });

  test('FooBar contains foo bar', () => {
    YOUR TEST CODE GOES HERE
  });

完成上述步骤后,在您的React组件FooBar.js中使用useAuth0时,它将正确模拟并调用您的mockAuth0函数,并返回您在上面定义的值。

如果您正在阅读此内容并需要帮助,希望这可以帮助您,并且也可以为您工作。 :)

祝您一切顺利,

-Gabriel


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