如何使用axios进行React应用程序的API调用测试?

huangapple go评论86阅读模式
英文:

How to test a react app with an axios api call?

问题

I will provide translations for the code parts you've provided.

在这里是测试的代码部分:

  1. 我正在尝试测试一个组件该组件渲染从Unsplash API使用axios获取的图像但是测试一直失败但应用程序却正常运行
  2. 测试抛出的错误是
  3. 预期元素应具有属性
  4. src="https://via.placeholder.com/300x300.png?text=robin"
  5. 收到的是
  6. src="https://via.placeholder.com/200x300/86efac?text=Loading..."
  7. 我认为这必须意味着我没有正确模拟axios请求或者与延迟加载有关吗
  8. 我的测试代码
  9. // 这里是测试代码,包括所需的导入和模拟 axios 请求的部分

接下来是要测试的组件:

  1. // 这是要测试的组件代码

最后是API调用的部分:

  1. // 这是API调用的代码

请告诉我如果您需要更多信息或有其他翻译请求。

英文:

I'm trying to test a component that renders an image that's fetched from the unpsplash api using axios. However the test keeps failing but the app is working.

The error the test throws is:
Expected the element to have attribute:
src="https://via.placeholder.com/300x300.png?text=robin"
Received:
src="https://via.placeholder.com/200x300/86efac?text=Loading..."

I think this must mean I'm not mocking the axios request properly or is it something to do with lazy loading?

My test:

  1. import { screen, waitFor } from "@testing-library/react";
  2. import { renderWithProviders } from "../utils/utils-for-tests";
  3. import axios from "axios";
  4. import BirdImg from "../components/BirdImg";
  5. class IntersectionObserver {
  6. observe() {
  7. return null;
  8. }
  9. disconnect() {
  10. return null;
  11. }
  12. }
  13. window.IntersectionObserver = IntersectionObserver;
  14. jest.mock("axios", () => ({
  15. get: jest.fn(),
  16. }));
  17. const mockLoadedResponse = {
  18. data: {
  19. results: [
  20. {
  21. urls: {
  22. thumb: "https://via.placeholder.com/300x300.png?text=robin",
  23. },
  24. },
  25. ],
  26. },
  27. };
  28. test("shows the image", async () => {
  29. axios.get.mockResolvedValue(mockLoadedResponse);
  30. renderWithProviders(<BirdImg name="robin" />);
  31. const birdImage = await screen.findByRole("img");
  32. await waitFor(() => {
  33. expect(birdImage).toBeInTheDocument();
  34. expect(birdImage).toHaveAttribute(
  35. "src",
  36. "https://via.placeholder.com/300x300.png?text=robin"
  37. );
  38. });
  39. });

The component I want to test:

  1. import { useEffect, useRef, useState } from "react";
  2. import { fetchBirdImg } from "../api/unsplash";
  3. function BirdImg({ name }) {
  4. const imgRef = useRef(null);
  5. const [loaded, setLoaded] = useState(false);
  6. const [error, setError] = useState(false);
  7. useEffect(() => {
  8. const observer = new IntersectionObserver((entries) => {
  9. entries.forEach((entry) => {
  10. if (entry.isIntersecting) {
  11. fetchBirdImg(name).then((data) => {
  12. imgRef.current.src = data[0].urls.thumb;
  13. setLoaded(true);
  14. });
  15. observer.unobserve(imgRef.current);
  16. }
  17. });
  18. });
  19. observer.observe(imgRef.current);
  20. return () => observer.disconnect();
  21. }, [name]);
  22. let imgSrc;
  23. if (loaded === false) {
  24. imgSrc = `https://via.placeholder.com/200x300/86efac?text=Loading...`;
  25. }
  26. if (loaded) {
  27. imgSrc = imgRef.current.src;
  28. }
  29. if (error === true) {
  30. imgSrc = `https://via.placeholder.com/200x300/86efac?text=Error`;
  31. }
  32. return (
  33. <img
  34. ref={imgRef}
  35. onLoad={() => {
  36. setLoaded(true);
  37. }}
  38. onError={() => {
  39. setError(true);
  40. }}
  41. src={imgSrc}
  42. alt={name}
  43. className="w-20"
  44. />
  45. );
  46. }
  47. export default BirdImg;
  1. The api call:
  2. import axios from "axios";
  3. async function fetchBirdImg(name) {
  4. const response = await axios.get(
  5. "https://api.unsplash.com/search/photos?per_page=1&orientation=portrait",
  6. {
  7. headers: {
  8. Authorization: "auth key",
  9. },
  10. params: { query: `${name}` },
  11. }
  12. );
  13. return response.data.results;
  14. }
  15. export { fetchBirdImg };

答案1

得分: 1

以下是您要翻译的内容:

You should also mock IntersectionObserver() constructor and unobserve() method

You can simply mock the fetchBirdImg API function instead of the axios.

E.g.

Directory structure:

  1. tree -L 2 -I 'node_modules'
  2. .
  3. ├── BirdImg.jsx
  4. ├── BirdImg.test.jsx
  5. └── api.js

BirdImg.test.jsx:

  1. import React from 'react';
  2. import { screen, waitFor, render } from '@testing-library/react';
  3. import '@testing-library/jest-dom';
  4. import BirdImg from './BirdImg';
  5. import { fetchBirdImg } from './api';
  6. jest.mock('./api');
  7. const mockLoadedResponse = [{ urls: { thumb: 'https://via.placeholder.com/300x300.png?text=robin' } }];
  8. test('shows the image', async () => {
  9. let _callback;
  10. const intersectionObserverEntries = [{ isIntersecting: true }];
  11. class IntersectionObserver {
  12. constructor(callback) {
  13. _callback = callback;
  14. }
  15. observe() {
  16. return null;
  17. }
  18. unobserve() {
  19. return null;
  20. }
  21. disconnect() {
  22. return null;
  23. }
  24. }
  25. fetchBirdImg.mockResolvedValueOnce(mockLoadedResponse);
  26. window.IntersectionObserver = IntersectionObserver;
  27. render(<BirdImg name="robin" />);
  28. const birdImage = screen.getByRole('img');
  29. expect(birdImage).toHaveAttribute('src', 'https://via.placeholder.com/200x300/86efac?text=Loading...');
  30. _callback(intersectionObserverEntries);
  31. await waitFor(() => {
  32. expect(birdImage).toBeInTheDocument();
  33. expect(birdImage).toHaveAttribute('src', 'https://via.placeholder.com/300x300.png?text=robin');
  34. });
  35. });

我们声明了一个 _callback 变量来保存真正的回调函数,并在 IntersectionObserver 类实例化后手动调用它,传入模拟的 intersectionObserverEntries 对象。

测试结果:

  1. PASS stackoverflow/76189869/BirdImg.test.jsx (20.943 s)
  2. shows the image (89 ms)
  3. -------------|---------|----------|---------|---------|-------------------
  4. File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
  5. -------------|---------|----------|---------|---------|-------------------
  6. All files | 83.87 | 75 | 66.67 | 86.21 |
  7. BirdImg.jsx | 88.89 | 75 | 75 | 92 | 34,38
  8. api.js | 50 | 100 | 0 | 50 | 4-13
  9. -------------|---------|----------|---------|---------|-------------------
  10. Test Suites: 1 passed, 1 total
  11. Tests: 1 passed, 1 total
  12. Snapshots: 0 total
  13. Time: 22.404 s
  14. Ran all test suites related to changed files.
英文:

You should also mock IntersectionObserver() constructor and unobserve() method

You can simply mock the fetchBirdImg API function instead of the axios.

E.g.

Directory structure:

  1. tree -L 2 -I &#39;node_modules&#39;
  2. .
  3. ├── BirdImg.jsx
  4. ├── BirdImg.test.jsx
  5. └── api.js

BirdImg.test.jsx:

  1. import React from &#39;react&#39;;
  2. import { screen, waitFor, render } from &#39;@testing-library/react&#39;;
  3. import &#39;@testing-library/jest-dom&#39;;
  4. import BirdImg from &#39;./BirdImg&#39;;
  5. import { fetchBirdImg } from &#39;./api&#39;;
  6. jest.mock(&#39;./api&#39;);
  7. const mockLoadedResponse = [{ urls: { thumb: &#39;https://via.placeholder.com/300x300.png?text=robin&#39; } }];
  8. test(&#39;shows the image&#39;, async () =&gt; {
  9. let _callback;
  10. const intersectionObserverEntries = [{ isIntersecting: true }];
  11. class IntersectionObserver {
  12. constructor(callback) {
  13. _callback = callback;
  14. }
  15. observe() {
  16. return null;
  17. }
  18. unobserve() {
  19. return null;
  20. }
  21. disconnect() {
  22. return null;
  23. }
  24. }
  25. fetchBirdImg.mockResolvedValueOnce(mockLoadedResponse);
  26. window.IntersectionObserver = IntersectionObserver;
  27. render(&lt;BirdImg name=&quot;robin&quot; /&gt;);
  28. const birdImage = screen.getByRole(&#39;img&#39;);
  29. expect(birdImage).toHaveAttribute(&#39;src&#39;, &#39;https://via.placeholder.com/200x300/86efac?text=Loading...&#39;);
  30. _callback(intersectionObserverEntries);
  31. await waitFor(() =&gt; {
  32. expect(birdImage).toBeInTheDocument();
  33. expect(birdImage).toHaveAttribute(&#39;src&#39;, &#39;https://via.placeholder.com/300x300.png?text=robin&#39;);
  34. });
  35. });

We declared a _callback variable to hold the real callback and call it manually with mocked intersectionObserverEntries object after the instantiation of the IntersectionObserver class.

Test result:

  1. PASS stackoverflow/76189869/BirdImg.test.jsx (20.943 s)
  2. shows the image (89 ms)
  3. -------------|---------|----------|---------|---------|-------------------
  4. File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
  5. -------------|---------|----------|---------|---------|-------------------
  6. All files | 83.87 | 75 | 66.67 | 86.21 |
  7. BirdImg.jsx | 88.89 | 75 | 75 | 92 | 34,38
  8. api.js | 50 | 100 | 0 | 50 | 4-13
  9. -------------|---------|----------|---------|---------|-------------------
  10. Test Suites: 1 passed, 1 total
  11. Tests: 1 passed, 1 total
  12. Snapshots: 0 total
  13. Time: 22.404 s
  14. Ran all test suites related to changed files.

huangapple
  • 本文由 发表于 2023年5月7日 00:03:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/76189869.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定