英文:
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 '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');
});
});
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论