如何使用 Jest 在 React 组件中测试/模拟 fetch api?

Geo*_*ana 4 javascript testing unit-testing reactjs jestjs

我是测试驱动开发的新手,我遇到了有关测试/模拟 fetch api 的部分。但我正在努力编写自己的测试。我构建了一个简单的天气应用程序,只是为了使用笑话来测试/模拟获取。但测试总是失败。我不断收到如下错误:

\n

无效的挂钩调用。钩子只能在函数组件的主体内部调用。发生这种情况可能是由于以下原因之一: 不仅如此,我不知道我哪里出了问题,所以我来这里寻求有关如何模拟/改进我的测试以使其成功的提示。H

\n

这是我的 React 代码:(App.js)

\n
  const [search, setSearch] = useState('');\n  const [weather, setWeather] = useState({}); \n  \n  const handleChange = (e) => {\n    setSearch(e.target.value)\n  }\n\n //function returns a promise\n  const WeatherData = async (e) => {\n    if (e.key === "Enter") {\n      await fetch(`${api.baseURL}weather?q=${search}&appid=${api.key}`)\n        .then(data => data.json())\n        .then(city => {\n          //console.log(city)\n          setSearch('')\n          setWeather(city)\n        })\n    }\n  }\n\n  const currentDate = (d) => {\n    let months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];\n    let days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];\n\n    let day = days[d.getDay()];\n    let month = months[d.getMonth()];\n    let year = d.getFullYear();\n    let date = d.getDate();\n\n    return `${day} ${date} ${month} ${year}`\n\n  }\n\n  return (\n    <div className="App">\n      <h2>International Weather</h2>\n      <div className="wrapper">\n        <input type="text" id="search-field" placeholder='Search...' onChange={handleChange} onKeyPress={WeatherData} />\n\n        {(typeof weather.main != "undefined") ? (\n\n          <div className='weather-box'>\n            <h2>{weather.name}, {weather.sys.country}</h2>\n            <h2> {currentDate(new Date())} </h2>\n\n            <div id="weather">\n\n              <div className="details" id="degrees">{(weather.main.temp - 273.15).toFixed(2)}\xc2\xb0C</div>\n              <div className="details" id="clouds">{weather.weather[0].main}</div>\n\n            </div>\n          </div>\n\n        ) : (" ")}\n\n      </div>\n    </div>\n  );\n}\n
Run Code Online (Sandbox Code Playgroud)\n

我的 App.js 代码:

\n
import { render, screen } from "@testing-library/react";\nimport App from "./App";\n\n//creating a snapshot test to test if the rendered component is the same as the snapshot app\ntest("snapshot is correct", () => {\n  const tree = render(<App />);\n  expect(tree).toMatchSnapshot();\n});\n\n//test whether the function works\ntest("fetch works correctly", async () => {\n  App(\n    JSON.stringify({\n      results: [{ user: "mandla", age: 43 }],\n    })\n  ).then((data) => {\n    expect(data).toBe();\n  });\n});\n
Run Code Online (Sandbox Code Playgroud)\n

如果有人可以帮助我理解问题以及为什么我的解决方案不起作用,我将不胜感激。

\n

小智 5

您可以通过以下任意方法测试 fetch API。

  1. 模拟获取
// This is the function we'll be testing
async function withFetch() {
  const res = await fetch('https://jsonplaceholder.typicode.com/posts')
  const json = await res.json()

  return json
}

// This is the section where we mock `fetch`
const unmockedFetch = global.fetch

beforeAll(() => {
  global.fetch = () =>
    Promise.resolve({
      json: () => Promise.resolve([]),
    })
})

afterAll(() => {
  global.fetch = unmockedFetch
})

// This is actual testing suite
describe('withFetch', () => {
  test('works', async () => {
    const json = await withFetch()
    expect(Array.isArray(json)).toEqual(true)
    expect(json.length).toEqual(0)
  })
})
Run Code Online (Sandbox Code Playgroud)
  1. 玩笑间谍软件
const fetchMock = jest
  .spyOn(global, 'fetch')
  .mockImplementation(() =>
    Promise.resolve({ json: () => Promise.resolve([]) })
  )

describe('withFetch', () => {
  test('works', async () => {
    const json = await withFetch()

    // highlight-start
    expect(fetchMock).toHaveBeenCalledWith(
      'https://jsonplaceholder.typicode.com/posts'
    )
    // highlight-end

    expect(Array.isArray(json)).toEqual(true)
    expect(json.length).toEqual(0)
  })
})
Run Code Online (Sandbox Code Playgroud)

请查看下面的链接

https://benjaminjohnson.me/mocking-fetch