如何使用 SWR 编写测试

10
当用户在搜索输入框中输入文本时,需要删除 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个回答

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

如果我的项目中没有axios,只有swr怎么办?我需要安装axios来测试swr吗? - Mega Proger
@MegaProger 使用 msw - Lin Du
2
我们能否使用相同的逻辑来模拟 fetch 而不是 axios? - Raphael Pinel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接