如何使用 swr 编写测试

Mii*_*nny 5 reactjs jestjs react-testing-library react-hooks swr

当用户在输入搜索中输入一些文本时,必须删除 data-testid = 'loading',现在控制台返回 Unable to find an element by: [data-testid="loading"] 有人可以建议我用 swr 或建议编写测试对我来说如何模拟来自 swr 的响应

这是我的组件文件

import axios from "axios";
import { useEffect, useState } from "react";
import useSWR from "swr";
import "./styles.css";

const useDebounce = (newValue) => {
  const [value, setValue] = useState("");
  useEffect(() => {
    const timeout = setTimeout(() => {
      setValue(newValue);
    }, 500);
    return () => clearTimeout(timeout);
  }, [newValue]);
  return value;
};

export const fetcher = async (url) => {
  const { data } = await axios.get(url);
  return data;
};
export default function App() {
  const [query, setQuery] = useState("react");
  const searchQuery = useDebounce(query);
  const { data: repos } = useSWR(
    `https://api.github.com/search/repositories?q=${searchQuery}&per_page=1&page=1`,
    fetcher
  );
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <input
        testId="search"
        data-testid="search"
        id="search-input"
        placeholder="search"
        onChange={(event) => setQuery(event.target.value)}
      />
      {!repos ? (
        <div data-testid="loader" testId="loader">
          <h2 id="loading">loading</h2>
        </div>
      ) : (
        repos?.items?.map((user) => {
          return (
            <div
              data-testid="repo-item"
              style={{
                margin: "1rem",
                height: "40px",
                background: "lightpink"
              }}
            >
              {user.name}
            </div>
          );
        })
      )}
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

这是我的测试文件

import {
  render,
  screen,
  waitForElementToBeRemoved
} from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import App from "./App";
import { act } from "react-dom/test-utils";

describe("test", () => {
  beforeEach(() => {
    jest.resetAllMocks();
    jest.useFakeTimers();
  });
  it("happy render", () => {
    expect(() => render(<App />)).not.toThrow();
  });

  it("renders after search", async () => {
    render(<App />);
    userEvent.type(screen.getByTestId("search"), "vue");
    act(() => {
      jest.runAllTimers();
    });
    await waitForElementToBeRemoved(() => screen.getByTestId("loader"));
  });
});
Run Code Online (Sandbox Code Playgroud)

sli*_*wp2 2

您可以模拟axios.get()方法及其已解析/拒绝的值,而不是模拟useSWR挂钩。然后,测试组件的行为,例如返回数据时呈现的内容以及没有可用数据时呈现的内容。

\n

例如

\n

App.tsx

\n
import axios from 'axios';\nimport React from 'react';\nimport { useEffect, useState } from 'react';\nimport useSWR from 'swr';\n\nconst useDebounce = (newValue) => {\n  const [value, setValue] = useState('');\n  useEffect(() => {\n    const timeout = setTimeout(() => {\n      setValue(newValue);\n    }, 500);\n    return () => clearTimeout(timeout);\n  }, [newValue]);\n  return value;\n};\n\nexport const fetcher = async (url) => {\n  const { data } = await axios.get(url);\n  return data;\n};\n\nexport default function App() {\n  const [query, setQuery] = useState('react');\n  const searchQuery = useDebounce(query);\n  const { data: repos } = useSWR(\n    `https://api.github.com/search/repositories?q=${searchQuery}&per_page=1&page=1`,\n    fetcher\n  );\n  return (\n    <div className="App">\n      <h1>Hello CodeSandbox</h1>\n      <h2>Start editing to see some magic happen!</h2>\n      <input\n        data-testid="search"\n        id="search-input"\n        placeholder="search"\n        onChange={(event) => setQuery(event.target.value)}\n      />\n      {!repos ? (\n        <div data-testid="loader">\n          <h2 id="loading">loading</h2>\n        </div>\n      ) : (\n        repos?.items?.map((user) => {\n          console.log('user: ', user);\n          return (\n            <div key={user.id} data-testid="repo-item">\n              {user.name}\n            </div>\n          );\n        })\n      )}\n    </div>\n  );\n}\n
Run Code Online (Sandbox Code Playgroud)\n

App.test.tsx

\n
import React from 'react';\nimport { render, screen, waitForElementToBeRemoved } from '@testing-library/react';\nimport userEvent from '@testing-library/user-event';\nimport App from './App';\nimport { act } from 'react-dom/test-utils';\nimport axios from 'axios';\n\ndescribe('test', () => {\n  beforeEach(() => {\n    jest.clearAllMocks();\n    jest.useFakeTimers();\n  });\n  it('happy render', () => {\n    expect(() => render(<App />)).not.toThrow();\n  });\n\n  it('renders after search', async () => {\n    jest.spyOn(axios, 'get').mockResolvedValueOnce({\n      data: {\n        items: [\n          { id: 1, name: 'react' },\n          { id: 2, name: 'jestjs' },\n        ],\n      },\n    });\n    render(<App />);\n    userEvent.type(screen.getByTestId('search'), 'vue');\n    act(() => {\n      jest.runAllTimers();\n    });\n    await waitForElementToBeRemoved(() => screen.getByTestId('loader'));\n  });\n});\n
Run Code Online (Sandbox Code Playgroud)\n

测试结果:

\n
 PASS  examples/67958776/App.test.tsx (8.166 s)\n  test\n    \xe2\x9c\x93 happy render (42 ms)\n    \xe2\x9c\x93 renders after search (58 ms)\n\n  console.log\n    user:  { id: 1, name: 'react' }\n\n      at examples/67958776/App.tsx:45:19\n          at Array.map (<anonymous>)\n\n  console.log\n    user:  { id: 2, name: 'jestjs' }\n\n      at examples/67958776/App.tsx:45:19\n          at Array.map (<anonymous>)\n\n----------|---------|----------|---------|---------|-------------------\nFile      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s \n----------|---------|----------|---------|---------|-------------------\nAll files |     100 |       80 |     100 |     100 |                   \n App.tsx  |     100 |       80 |     100 |     100 | 44                \n----------|---------|----------|---------|---------|-------------------\nTest Suites: 1 passed, 1 total\nTests:       2 passed, 2 total\nSnapshots:   0 total\nTime:        8.873 s\n
Run Code Online (Sandbox Code Playgroud)\n

  • 我们可以使用相同的逻辑来模拟“fetch”而不是 axios 吗? (3认同)