在酶中编写服务器错误场景的单元测试

发布于 2025-01-11 10:43:25 字数 5229 浏览 2 评论 0原文

我正在开发一项功能,该功能涉及从 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 测试库 更可取,但我的组织尚未转向这一点。我还必须编写失败案例,因为分支的覆盖范围很差。我将非常感谢任何帮助。谢谢

参考链接和问题:

  1. 如何使用 jest-and-enzyme 测试异步数据获取反应组件
  2. 使用 mount 测试钩子
  3. 应该Promise.reject 消息是否包含在 Error 中?

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:

  1. how-to-test-async-data-fetching-react-component-using-jest-and-enzyme
  2. Testing hooks with mount
  3. Should a Promise.reject message be wrapped in Error?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文