React:如何模拟 Auth0 以使用 Jest 进行测试

tmo*_*mos 5 javascript typescript reactjs jestjs auth0

我正在使用 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);
  });
});
Run Code Online (Sandbox Code Playgroud)

但我最终陷入了这个错误:

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

我在这里有点迷茫,任何帮助将不胜感激!

小智 12

我自己花了一个小时左右解决这个问题。问题源于修改模拟返回值后应用于 useAuth0 的错误类型。我的解决方案是使用“ts-jest/utils”中的模拟。您还可以将角色、范围等添加到用户对象。(参见 adminUser 对象)

更新 2022-11-21自版本 28.0+ 起,该mocked功能已从软件包中删除(请参阅v27 文档ts-jestmocked

该函数现已弃用,并将在28.0.0中删除。该功能已作为 Jest 27.4.0jest-mock的一部分集成到包中,请参阅https://github.com/facebook/jest/pull/12089。请使用来自的替代。jest-mock

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();
   });
});
Run Code Online (Sandbox Code Playgroud)


jer*_*ome 6

阅读这篇文章后,遇到类似的错误,并更仔细地调查笑话文档......

我能够在使用正常的 Auth0 导入和标准 jest 模拟功能时模拟 useAuth0() 挂钩。

如果此解决方案有改进或存在问题,请告诉我,因为我是使用 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;
Run Code Online (Sandbox Code Playgroud)

第 2 步:在 FooBar.test.js 中,创建一个用户、登录和注销的值以及模拟 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;
};
Run Code Online (Sandbox Code Playgroud)

第 3 步:在 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))
  };
});
Run Code Online (Sandbox Code Playgroud)

第 4 步:在 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
  });
Run Code Online (Sandbox Code Playgroud)

完成此操作后,在您拥有 useAuth0 的 React 组件 FooBar.js 中,它将正确模拟并调用您的 mockAuth0 函数并返回您上面定义的值。

如果您正在阅读本文并需要帮助,我希望这对您有所帮助,并且它也对您有用。:)

祝你一切顺利,

-加布里埃尔


bal*_*fin 5

这是一个打字稿错误。您将需要键入模拟,useAuth0因为原始类型没有称为mockReturnValue. 这样的事情应该工作:

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

mockedUseAuth0.mockReturnValue({
  isAuthenticated: true,
  user,
  logout: jest.fn(),
  loginWithRedirect: jest.fn()
});
Run Code Online (Sandbox Code Playgroud)