在酶中编写服务器错误场景的单元测试
我正在开发一项功能,该功能涉及从 API 获取数据并相应地渲染组件。
index.js
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import StarRating from './components/StarRating';
import {
API_URL,
API_TIMEOUT,
} from '../../../../someLoc';
export async function fetchWithTimeout(resource, options = {}) {
const { timeout } = options;
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
const response = await fetch(resource, {
...options,
signal: controller.signal,
});
clearTimeout(id);
return response;
}
const UserRating = ({ productId }) => {
const [loading, setLoading] = useState(true);
const [data, setData] = useState({});
const [error, setError] = useState(false);
useEffect(
() => {
const fetchReviewsNRatings = async (bvUrl, bvTimeout) => {
const url = `${bvUrl}:${productId}`;
const response = await fetchWithTimeout(url, { timeout: bvTimeout });
if (response.ok) {
const ratingData = await response.json();
setData(ratingData);
setLoading(false);
} else {
setError(true);
setLoading(false);
}
};
fetchReviewsNRatings(API_TIMEOUT);
},
[productId],
);
// I didn't know how to test this branch if I simply returned null. so wrapped null inside of div.
// MY INTENT HERE: if(loading) return null;
if (loading) return <div className="test-class">{null}</div>;
// destructuring the response to get the data once loading is complete.
const {
Results: [
{
ABC: {
DEF: { GHI, JKL },
},
},
],
} = data;
const numReviews = +GHI;
const value = JKL.toFixed(1);
return !error && <StarRating value={value} numReviews={numReviews} />;
};
UserRating.propTypes = {
productId: PropTypes.string,
};
export default UserRating;
index.test.js
import React from 'react';
import { mount } from 'enzyme';
import { act } from 'react-dom/test-utils';
import UserRating from '..';
import { PRODUCT_ID } from '../../../../../someLoc';
describe('Testing for <UserRating /> component', () => {
const data = {
Results: [
{
ABC: {
DEF: {
GHI: 4.567,
JKL: 1103,
},
},
},
],
};
const productId = PRODUCT_ID;
let component = null;
beforeEach(() => {
global.fetchWithTimeout = jest.fn(() =>
Promise.resolve({
ok: true,
status: 200,
data,
json: () => data,
}),
);
global.fetch = jest.fn(() =>
Promise.resolve({
ok: true,
status: 200,
data,
json: () => data,
}),
);
component = act(() => mount(<UserRating productId={productId} />));
});
it('should call fetch', async () => {
expect(component.exists()).toBeTruthy();
// Testting for loading state.
expect(component.find('.test-class').exists()).toBe(true);
});
什么按预期工作:
- 预期的功能。
- 我编写的上述测试是通过双赢场景,即当数据加载和获取成功时。
我的问题:
我想以这种方式编写一个单元测试,以便测试如果服务器端(5xx)或客户端(4xx)出现错误会发生什么。
b.在通过测试场景中我还可以改进哪些方面?
我尝试过的:
describe('Testing for <UserRating /> component for failed response', () => {
const data = { message: '500: Internal Server Error' };
const error = true;
const productId = PRODUCT_ID;
let component = null;
beforeEach(() => {
global.fetchWithTimeout = jest.fn(() =>
Promise.reject(
new Error({
ok: false,
status: 500,
data,
json: () => data,
}),
),
);
global.fetch = jest.fn(() =>
Promise.reject(
new Error({
ok: false,
status: 500,
data,
json: () => data,
}),
),
);
component = act(() => mount(<UserRating productId={productId} />));
});
it('should not fetch', async () => {
expect(component.exists()).toBeFalsy();
expect(component.find('.test-class').exists()).toBe(true);
console.debug(component);
});
});
当我尝试上面的代码时,它也破坏了双赢场景,并出现错误消息无法读取 null 的属性。我对酶比较陌生,在使用酶为钩子编写测试时面临问题。我知道 React 测试库 更可取,但我的组织尚未转向这一点。我还必须编写失败案例,因为分支的覆盖范围很差。我将非常感谢任何帮助。谢谢
参考链接和问题:
I was working on a functionality which involved fetching the data from an API and rendering the components accordingly.
index.js
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import StarRating from './components/StarRating';
import {
API_URL,
API_TIMEOUT,
} from '../../../../someLoc';
export async function fetchWithTimeout(resource, options = {}) {
const { timeout } = options;
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
const response = await fetch(resource, {
...options,
signal: controller.signal,
});
clearTimeout(id);
return response;
}
const UserRating = ({ productId }) => {
const [loading, setLoading] = useState(true);
const [data, setData] = useState({});
const [error, setError] = useState(false);
useEffect(
() => {
const fetchReviewsNRatings = async (bvUrl, bvTimeout) => {
const url = `${bvUrl}:${productId}`;
const response = await fetchWithTimeout(url, { timeout: bvTimeout });
if (response.ok) {
const ratingData = await response.json();
setData(ratingData);
setLoading(false);
} else {
setError(true);
setLoading(false);
}
};
fetchReviewsNRatings(API_TIMEOUT);
},
[productId],
);
// I didn't know how to test this branch if I simply returned null. so wrapped null inside of div.
// MY INTENT HERE: if(loading) return null;
if (loading) return <div className="test-class">{null}</div>;
// destructuring the response to get the data once loading is complete.
const {
Results: [
{
ABC: {
DEF: { GHI, JKL },
},
},
],
} = data;
const numReviews = +GHI;
const value = JKL.toFixed(1);
return !error && <StarRating value={value} numReviews={numReviews} />;
};
UserRating.propTypes = {
productId: PropTypes.string,
};
export default UserRating;
index.test.js
import React from 'react';
import { mount } from 'enzyme';
import { act } from 'react-dom/test-utils';
import UserRating from '..';
import { PRODUCT_ID } from '../../../../../someLoc';
describe('Testing for <UserRating /> component', () => {
const data = {
Results: [
{
ABC: {
DEF: {
GHI: 4.567,
JKL: 1103,
},
},
},
],
};
const productId = PRODUCT_ID;
let component = null;
beforeEach(() => {
global.fetchWithTimeout = jest.fn(() =>
Promise.resolve({
ok: true,
status: 200,
data,
json: () => data,
}),
);
global.fetch = jest.fn(() =>
Promise.resolve({
ok: true,
status: 200,
data,
json: () => data,
}),
);
component = act(() => mount(<UserRating productId={productId} />));
});
it('should call fetch', async () => {
expect(component.exists()).toBeTruthy();
// Testting for loading state.
expect(component.find('.test-class').exists()).toBe(true);
});
What's working as expected:
- The functionality that is expected.
- The above test which I managed to write is passing for win-win scenario i.e. when the data is loaded and fetched successfully.
MY QUESTION:
a. I want to write a unit test in such a way so as to test what happens if there is some error from server side(5xx) or from client side(4xx).
b. What else can I improve in my passing test scenario?
What I have tried:
describe('Testing for <UserRating /> component for failed response', () => {
const data = { message: '500: Internal Server Error' };
const error = true;
const productId = PRODUCT_ID;
let component = null;
beforeEach(() => {
global.fetchWithTimeout = jest.fn(() =>
Promise.reject(
new Error({
ok: false,
status: 500,
data,
json: () => data,
}),
),
);
global.fetch = jest.fn(() =>
Promise.reject(
new Error({
ok: false,
status: 500,
data,
json: () => data,
}),
),
);
component = act(() => mount(<UserRating productId={productId} />));
});
it('should not fetch', async () => {
expect(component.exists()).toBeFalsy();
expect(component.find('.test-class').exists()).toBe(true);
console.debug(component);
});
});
When I tried the above code it is breaking the win-win scenario as well with the error message cannot read property exists for null. I am relatively new to enzyme and facing issues while using enzyme for writing tests for hooks. I know that React testing library is preferrable but my organization is yet to shift onto that. I've to write fail case as well since the coverage for branches is poor. I'll be highly obliged for any help. Thanks
REFFERED LINKS and Questions:
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论