首页 > 解决方案 > 如何在反应中使用 API 调用测试组件?

问题描述

我在测试从 API 获取数据并在屏幕上呈现的反应组件时遇到问题。这是我得到的错误。

     ● Company Details › renders company details with given data

    expect(received).toMatch(expected)

    Expected substring: "Google"
    Received string:    "no name provided"

      18 |     );
      19 | 
    > 20 |     expect(getByTestId('company-name').textContent).toMatch('Google');
         |                                                     ^
      21 |     expect(getByTestId('sponsors-visa').textContent).toMatch('Yes');
      22 |     expect(getByTestId('description').textContent).toMatch('this is a company description');
      23 |   });

      at Object.<anonymous> (src/tests/components/company-detail/CompanyDetail.test.js:20:53)

我的测试文件CompanyDetail.test.js代码:

    import React from 'react';
    import CompanyDetail from '../../../components/company-detail/CompanyDetail';
    import { BrowserRouter } from 'react-router-dom';
    import { render } from '@testing-library/react';
    describe('Company Details', () => {
      let mockData;
      beforeEach(() => {
        mockData = { match: { params: { id: 4 } } };
        jest.mock('../../../components/effects/use-fetch.effect');
      });

      it('renders company details with given data', async () => {
        const { getByTestId } = render(
          <BrowserRouter>
            <CompanyDetail {...mockData} />,
          </BrowserRouter>
        );

        expect(getByTestId('company-name').textContent).toMatch('Google');
        expect(getByTestId('sponsors-visa').textContent).toMatch('Yes');
        expect(getByTestId('description').textContent).toMatch('this is a company description');
      });
    });

我要测试的代码 ( CompanyDetail.js)

    import CONSTANTS from '../../constants/constants';
    import { Link } from 'react-router-dom';
    import useFetch from '../effects/use-fetch.effect';

    const CompanyDetail = (props) => {
      const { id } = props.match.params;
      const { name, description, jobs, known_to_sponsor_visa } = useFetch(`${CONSTANTS.BASE_URL}/companies/${id}`);
      return (
        <React.Fragment>
          <Container>
            <Row className="m-2">
              <Col>
                <Card>
                  <Card.Body>
                    <Card.Title>
                      <h3 data-testid="company-name">{name ? name : 'no name provided'}</h3>
                    </Card.Title>
                    <Card.Text data-testid="sponsors-visa">
                      <b>Known to sponsor work visa: </b>
                      {known_to_sponsor_visa ? known_to_sponsor_visa : 'No data'}
                    </Card.Text>
                    <Card.Text data-test-id="description">{description}</Card.Text>
                  </Card.Body>
                </Card>
              </Col>
            </Row>
          </Container>
        </React.Fragment>
      );
    };

    export default CompanyDetail;

以防万一,如果它需要use-fetch.effect.js

    import { useState, useEffect } from 'react';

    const useFetch = (url) => {
      const [dataArray, setData] = useState([]);

      useEffect(() => {
        try {
          const fetchData = async () => {
            const res = await fetch(url);
            const dataArray = await res.json();
            setData(dataArray.data)
          }
          fetchData();

        } catch (err) {
          console.error(err);
        }
      }, [url]);

      return dataArray;
    };

    export default useFetch;

我可以通过 props 发送数据来测试它,但我不知道如何模拟数据来测试该 url 以接收 id 并将其呈现到特定位置。任何帮助,将不胜感激。我知道为什么会出现错误,因为我没有通过任何公司名称作为 Google 进行检查。问题是我如何通过传递一些虚拟数据来测试它。

标签: reactjsjestjsfetchreact-hooksreact-testing-library

解决方案


由于useFetch预计会以任何方式异步,这可能会影响组件的工作方式,因此它需要以比返回值的 Jest spy 更复杂的方式进行模拟。请求本身可以被模拟而不是整个useFetch

let data = { name: 'Google', ... };
spyOn(global, 'fetch').mockResolvedValue({ json: jest.fn().mockResolvedValue({ data }) });

...

expect(getByTestId('company-name').textContent).toMatch('no name provided');
await waitFor(() => expect(getByTestId('company-name').textContent).toMatch('Google'));

推荐阅读