如何使用 swr 编写测试

Posted

技术标签:

【中文标题】如何使用 swr 编写测试【英文标题】:How to write test with swr 【发布时间】:2021-08-29 16:22:45 【问题描述】:

当用户在输入搜索中键入一些文本时 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>
  );

这是我的测试文件

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"));
  );
);

【问题讨论】:

【参考方案1】:

您可以模拟 axios.get() 方法及其解析/拒绝值,而不是模拟 useSWR 钩子。然后,测试组件的行为,比如返回数据时渲染了什么,没有数据时渲染了什么。

例如

App.tsx:

import axios from 'axios';
import React from 'react';
import  useEffect, useState  from 'react';
import useSWR from 'swr';

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
        data-testid="search"
        id="search-input"
        placeholder="search"
        onChange=(event) => setQuery(event.target.value)
      />
      !repos ? (
        <div data-testid="loader">
          <h2 id="loading">loading</h2>
        </div>
      ) : (
        repos?.items?.map((user) => 
          console.log('user: ', user);
          return (
            <div key=user.id data-testid="repo-item">
              user.name
            </div>
          );
        )
      )
    </div>
  );

App.test.tsx:

import React from 'react';
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';
import axios from 'axios';

describe('test', () => 
  beforeEach(() => 
    jest.clearAllMocks();
    jest.useFakeTimers();
  );
  it('happy render', () => 
    expect(() => render(<App />)).not.toThrow();
  );

  it('renders after search', async () => 
    jest.spyOn(axios, 'get').mockResolvedValueOnce(
      data: 
        items: [
           id: 1, name: 'react' ,
           id: 2, name: 'jestjs' ,
        ],
      ,
    );
    render(<App />);
    userEvent.type(screen.getByTestId('search'), 'vue');
    act(() => 
      jest.runAllTimers();
    );
    await waitForElementToBeRemoved(() => screen.getByTestId('loader'));
  );
);

测试结果:

 PASS  examples/67958776/App.test.tsx (8.166 s)
  test
    ✓ happy render (42 ms)
    ✓ renders after search (58 ms)

  console.log
    user:   id: 1, name: 'react' 

      at examples/67958776/App.tsx:45:19
          at Array.map (<anonymous>)

  console.log
    user:   id: 2, name: 'jestjs' 

      at examples/67958776/App.tsx:45:19
          at Array.map (<anonymous>)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |     100 |       80 |     100 |     100 |                   
 App.tsx  |     100 |       80 |     100 |     100 | 44                
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        8.873 s

【讨论】:

以上是关于如何使用 swr 编写测试的主要内容,如果未能解决你的问题,请参考以下文章

Next.js 使用 SWR 和 axios

在 SWR 中使用 mutate 重新验证数据 - 我应该使用哪个?

swr,解决 React 项目 API 调用的最好方案

使用sws_scale转换视频使用swr_convert转换音频

使用sws_scale转换视频使用swr_convert转换音频

期望 SWR 库返回缓存数据但没有发生