React 测试 - 组件访问 window 对象时出错

React test - error when component accesses window object

出于特定原因,我在 public 文件夹的 index.html 文件中使用了一个脚本,该脚本在 window 对象上创建了一个 属性。 属性 是一个异步函数。这是我在 public/index.html:

中创建的脚本
 <script>
      const txt = "Hello Word";

      async function waitText() {
        try {
          const data = await fetch(
            "https://jsonplaceholder.typicode.com/all/1"
          );
          const text = await data.json();
          returntext;
        } catch (error) {
          console.error(error);
          return "Error";
        }
      }

      Object.defineProperty(window, "waitText", { value: waitText });
    </script>

我的 App 组件首先渲染一个在 useState 中指定的 Loading... 文本,然后 useEffect 中的一个异步函数调用 windom 对象的函数,等待响应并执行 setState 并更改 Loading.. . 文本到它从 api 得到的那个。到目前为止,该应用程序可以正常运行。这里的代码是 App.tsx

import logo from "./logo.svg";
import "./App.css";
import { useEffect, useState } from "react";

function App() {
  const [txt, setTxt] = useState("Loading...");

  useEffect(() => {
    const getTxt = async() => {
      try {
        const text = await window.waitText();
        setTxt(text.title);
      } catch (error) {
        console.error(error);
      }
    };

    getTxt();
  }, []);

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>{txt}</p>
      </header>
    </div>
  );
}

export defaultApp;

到目前为止,该应用程序运行良好。问题是用 jest 和 react-testing-library 做测试。这里的文本代码:

import { render, screen } from '@testing-library/react';
import App from './App';

test('renders learn react link', async () => {
  render(<App />);
    screen.debug()
  const linkElement = await screen.findByText("delectus aut autem");
  expect(linkElement).toBeInTheDocument();
});

当运行这个测试时,调用window.waitText()时在useEffect区域出现错误。显然,在执行玩笑测试时,它不会执行 index.html 中的脚本,因此 属性 永远不会在 window 对象中创建,因此在 useEffect 和文字无效。

从 public/index.html 中删除脚本不是解决方案。 好吧,虽然这不是一个好的做法,但在某些情况下这是必要的。

create-react-app 文档是这样说的:

Some libraries may be incompatible with webpack and you have no choice but to include it as a tag.

请问如何让这个组件通过测试?

您可以在测试用例中呈现组件之前模拟 window.waitText() 方法及其 resolve/rejected 值。

例如

App.jsx:

import { useEffect, useState } from 'react';
import React from 'react';

function App() {
  const [txt, setTxt] = useState('Loading...');

  useEffect(() => {
    const getTxt = async () => {
      try {
        const text = await window.waitText();
        setTxt(text.title);
      } catch (error) {
        console.error(error);
      }
    };

    getTxt();
  }, []);

  return (
    <div className="App">
      <header className="App-header">
        <p>{txt}</p>
      </header>
    </div>
  );
}

export default App;

App.test.jsx:

import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import React from 'react';
import App from './App';

test('renders learn react link', async () => {
  Object.defineProperty(window, 'waitText', { value: () => Promise.resolve({ title: 'delectus aut autem' }) });
  render(<App />);
  const linkElement = await screen.findByText('delectus aut autem');
  expect(linkElement).toBeInTheDocument();
});

测试结果:

 PASS  Whosebug/72355656/App.test.jsx (13.14 s)
  ✓ renders learn react link (33 ms)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |   92.31 |      100 |     100 |   91.67 |                   
 App.jsx  |   92.31 |      100 |     100 |   91.67 | 13                
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        14.9 s, estimated 15 s