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

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

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

问题

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

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

我正在尝试测试一个组件该组件渲染从Unsplash API使用axios获取的图像但是测试一直失败但应用程序却正常运行

测试抛出的错误是
预期元素应具有属性
      src="https://via.placeholder.com/300x300.png?text=robin"
    收到的是
      src="https://via.placeholder.com/200x300/86efac?text=Loading..."

我认为这必须意味着我没有正确模拟axios请求或者与延迟加载有关吗

我的测试代码

// 这里是测试代码,包括所需的导入和模拟 axios 请求的部分

接下来是要测试的组件:

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

最后是API调用的部分:

// 这是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:

import { screen, waitFor } from "@testing-library/react";
import { renderWithProviders } from "../utils/utils-for-tests";
import axios from "axios";
import BirdImg from "../components/BirdImg";

class IntersectionObserver {
  observe() {
    return null;
  }

  disconnect() {
    return null;
  }
}

window.IntersectionObserver = IntersectionObserver;

jest.mock("axios", () => ({
  get: jest.fn(),
}));

const mockLoadedResponse = {
  data: {
    results: [
      {
        urls: {
          thumb: "https://via.placeholder.com/300x300.png?text=robin",
        },
      },
    ],
  },
};
test("shows the image", async () => {
  axios.get.mockResolvedValue(mockLoadedResponse);

  renderWithProviders(<BirdImg name="robin" />);
  const birdImage = await screen.findByRole("img");

  await waitFor(() => {
    expect(birdImage).toBeInTheDocument();
    expect(birdImage).toHaveAttribute(
      "src",
      "https://via.placeholder.com/300x300.png?text=robin"
    );
  });
});

The component I want to test:

import { useEffect, useRef, useState } from "react";
import { fetchBirdImg } from "../api/unsplash";

function BirdImg({ name }) {
  const imgRef = useRef(null);
  const [loaded, setLoaded] = useState(false);
  const [error, setError] = useState(false);

  useEffect(() => {
    const observer = new IntersectionObserver((entries) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          fetchBirdImg(name).then((data) => {
            imgRef.current.src = data[0].urls.thumb;
            setLoaded(true);
          });

          observer.unobserve(imgRef.current);
        }
      });
    });

    observer.observe(imgRef.current);

    return () => observer.disconnect();
  }, [name]);

  let imgSrc;

  if (loaded === false) {
    imgSrc = `https://via.placeholder.com/200x300/86efac?text=Loading...`;
  }
  if (loaded) {
    imgSrc = imgRef.current.src;
  }
  if (error === true) {
    imgSrc = `https://via.placeholder.com/200x300/86efac?text=Error`;
  }

  return (
    <img
      ref={imgRef}
      onLoad={() => {
        setLoaded(true);
      }}
      onError={() => {
        setError(true);
      }}
      src={imgSrc}
      alt={name}
      className="w-20"
    />
  );
}

export default BirdImg;

The api call:

import axios from "axios";

async function fetchBirdImg(name) {
  const response = await axios.get(
    "https://api.unsplash.com/search/photos?per_page=1&orientation=portrait",

    {
      headers: {
        Authorization: "auth key",
      },
      params: { query: `${name}` },
    }
  );
  return response.data.results;
}

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:

⚡  tree -L 2 -I 'node_modules'
.
├── BirdImg.jsx
├── BirdImg.test.jsx
└── api.js

BirdImg.test.jsx:

import React from 'react';
import { screen, waitFor, render } from '@testing-library/react';
import '@testing-library/jest-dom';
import BirdImg from './BirdImg';
import { fetchBirdImg } from './api';

jest.mock('./api');

const mockLoadedResponse = [{ urls: { thumb: 'https://via.placeholder.com/300x300.png?text=robin' } }];
test('shows the image', async () => {
  let _callback;
  const intersectionObserverEntries = [{ isIntersecting: true }];
  class IntersectionObserver {
    constructor(callback) {
      _callback = callback;
    }
    observe() {
      return null;
    }
    unobserve() {
      return null;
    }
    disconnect() {
      return null;
    }
  }

  fetchBirdImg.mockResolvedValueOnce(mockLoadedResponse);
  window.IntersectionObserver = IntersectionObserver;

  render(<BirdImg name="robin" />);
  const birdImage = screen.getByRole('img');
  expect(birdImage).toHaveAttribute('src', 'https://via.placeholder.com/200x300/86efac?text=Loading...');
  _callback(intersectionObserverEntries);
  await waitFor(() => {
    expect(birdImage).toBeInTheDocument();
    expect(birdImage).toHaveAttribute('src', 'https://via.placeholder.com/300x300.png?text=robin');
  });
});

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

测试结果:

 PASS  stackoverflow/76189869/BirdImg.test.jsx (20.943 s)
  ✓ shows the image (89 ms)

-------------|---------|----------|---------|---------|-------------------
File         | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------|---------|----------|---------|---------|-------------------
All files    |   83.87 |       75 |   66.67 |   86.21 |                   
 BirdImg.jsx |   88.89 |       75 |      75 |      92 | 34,38             
 api.js      |      50 |      100 |       0 |      50 | 4-13              
-------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        22.404 s
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:

⚡  tree -L 2 -I &#39;node_modules&#39;
.
├── BirdImg.jsx
├── BirdImg.test.jsx
└── api.js

BirdImg.test.jsx:

import React from &#39;react&#39;;
import { screen, waitFor, render } from &#39;@testing-library/react&#39;;
import &#39;@testing-library/jest-dom&#39;;
import BirdImg from &#39;./BirdImg&#39;;
import { fetchBirdImg } from &#39;./api&#39;;

jest.mock(&#39;./api&#39;);

const mockLoadedResponse = [{ urls: { thumb: &#39;https://via.placeholder.com/300x300.png?text=robin&#39; } }];
test(&#39;shows the image&#39;, async () =&gt; {
  let _callback;
  const intersectionObserverEntries = [{ isIntersecting: true }];
  class IntersectionObserver {
    constructor(callback) {
      _callback = callback;
    }
    observe() {
      return null;
    }
    unobserve() {
      return null;
    }
    disconnect() {
      return null;
    }
  }

  fetchBirdImg.mockResolvedValueOnce(mockLoadedResponse);
  window.IntersectionObserver = IntersectionObserver;

  render(&lt;BirdImg name=&quot;robin&quot; /&gt;);
  const birdImage = screen.getByRole(&#39;img&#39;);
  expect(birdImage).toHaveAttribute(&#39;src&#39;, &#39;https://via.placeholder.com/200x300/86efac?text=Loading...&#39;);
  _callback(intersectionObserverEntries);
  await waitFor(() =&gt; {
    expect(birdImage).toBeInTheDocument();
    expect(birdImage).toHaveAttribute(&#39;src&#39;, &#39;https://via.placeholder.com/300x300.png?text=robin&#39;);
  });
});

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:

 PASS  stackoverflow/76189869/BirdImg.test.jsx (20.943 s)
  ✓ shows the image (89 ms)

-------------|---------|----------|---------|---------|-------------------
File         | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------|---------|----------|---------|---------|-------------------
All files    |   83.87 |       75 |   66.67 |   86.21 |                   
 BirdImg.jsx |   88.89 |       75 |      75 |      92 | 34,38             
 api.js      |      50 |      100 |       0 |      50 | 4-13              
-------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        22.404 s
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:

确定