如何使用 Jest 模拟记忆化的 React 组件

问题描述 投票:0回答:1

我想模拟一个已记忆的 React 组件。我正在使用 React 18.2 和 Jest 29.7 以及 Typescript,以防万一。我已经尝试了几种方法,但似乎找不到有效的方法。

这是我从哪里开始的一个最小示例:

// Foo.tsx
import Bar from "./Bar";

export default function Foo() {
  return <Bar />;
}
// Bar.tsx
import { memo } from "react";

function Bar() {
  return <div>original</div>;
}

export default memo(Bar);
//Foo.test.tsx
/**
 * @jest-environment jsdom
 */

import "@testing-library/jest-dom";
import Foo from "./Foo";
import { render, screen } from "@testing-library/react";

const barModule = require("./Bar");

describe("Foo", () => {
  beforeEach(() => {
    jest
      .spyOn(barModule, "default")
      .mockImplementation(() => <div>mocked</div>);
  });

  it("should mock Bar", () => {
    render(<Foo />);

    expect(screen.queryByText("mocked")).toBeInTheDocument();
  });
});

当Bar没有被记忆时,测试成功。但是通过调用备忘录,我收到此错误消息:

Cannot spy on the `default` property because it is not a function; object given instead.

互联网很快告诉我,我应该导出非记忆组件并模拟该组件。所以我这样做了(Foo.tsx 不变):

// Bar.tsx
import { memo } from "react";

export function Bar_() {
  return <div>original</div>;
}

export default memo(Bar_);
//Foo.test.tsx
/**
 * @jest-environment jsdom
 */

import "@testing-library/jest-dom";
import Foo from "./Foo";
import { render, screen } from "@testing-library/react";

const barModule = require("./Bar");

describe("Foo", () => {
  beforeEach(() => {
    jest
      .spyOn(barModule, "Bar_")
      .mockImplementation(() => <div>mocked</div>);
  });

  it("should mock Bar", () => {
    render(<Foo />);

    expect(screen.queryByText("mocked")).toBeInTheDocument();
  });
});

这会失败,因为由于某种原因没有应用模拟。使用原始的 Bar_ 实现。

互联网还建议模拟整个模块,所以我尝试了这个(其他两个文件不变):

//Foo.test.tsx
/**
 * @jest-environment jsdom
 */

import "@testing-library/jest-dom";
import Foo from "./Foo";
import { render, screen } from "@testing-library/react";

describe("Foo", () => {
  beforeEach(() => {
    jest.mock("./Bar", () => {
      const originalModule = jest.requireActual("./Bar");
      return {
        ...originalModule,
        Bar_: () => <div>Baba</div>,
      };
    });
  });

  it("should mock Bar", () => {
    render(<Foo />);

    screen.debug();
    expect(screen.queryByText("mocked")).toBeInTheDocument();
  });
});

由于此错误而失败:

The module factory of `jest.mock()` is not allowed to reference any out-of-scope variables.
Invalid variable access: _jsxFileName

我知道通常可以通过将变量重命名为以“mock”开头的名称来避免此错误。但这 _jsxFileName 是什么?

我在输入此内容时实际上已经想到了这一点。无论如何,我会保留这一步,以防有人也被困在这一点上。因此,我通过将模拟组件提取到名称以“Mock”开头的变量来修复此问题。现在我有这个:

//Foo.test.tsx
/**
 * @jest-environment jsdom
 */

import "@testing-library/jest-dom";
import Foo from "./Foo";
import { render, screen } from "@testing-library/react";

describe("Foo", () => {
  beforeEach(() => {
    const MockBar_ = () => <div>Baba</div>;

    jest.mock("./Bar", () => {
      const originalModule = jest.requireActual("./Bar");
      return {
        ...originalModule,
        Bar_: MockBar_,
      };
    });
  });

  it("should mock Bar", () => {
    render(<Foo />);

    screen.debug();
    expect(screen.queryByText("mocked")).toBeInTheDocument();
  });
});

这再次失败,因为没有应用模拟,而是使用了原始的 Bar_ 组件。

那么你实际上是如何做到这一点的呢? :-D

reactjs jestjs mocking memoization
1个回答
0
投票

您可以使用

jest.replaceProperty(object, propertyKey, value)
object[propertyKey]
替换为
value

例如

Bar.tsx

import React, { memo } from 'react';

function Bar() {
  return <div>original</div>;
}

export default memo(Bar);

Foo.tsx

import React from 'react';
import Bar from './Bar';

export default function Foo() {
  return <Bar />;
}

Foo.test.tsx

/**
 * @jest-environment jsdom
 */
import React from 'react';
import '@testing-library/jest-dom';
import Foo from './Foo';
import { render, screen } from '@testing-library/react';

const barModule = require('./Bar');

describe('Foo', () => {
  it('should mock Bar', () => {
    jest.replaceProperty(barModule, 'default', () => <div>mocked</div>);
    render(<Foo />);
    expect(screen.queryByText('mocked')).toBeInTheDocument();
  });
});

测试结果:

 PASS  stackoverflow/77770830/Foo.test.tsx
  Foo
    √ should mock Bar (17 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.11 s
Ran all test suites related to changed files.

套装版本:

"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"@testing-library/jest-dom": "^6.1.4",
"@testing-library/react": "^14.1.2",
"react": "^18.2.0",
© www.soinside.com 2019 - 2024. All rights reserved.