如何在反应中使用 API 调用测试组件?

Posted

技术标签:

【中文标题】如何在反应中使用 API 调用测试组件?【英文标题】:How to test component with API call in react? 【发布时间】:2020-09-19 20:54:49 【问题描述】:

我在测试从 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 进行检查。问题是我如何通过传递一些虚拟数据来测试它。

【问题讨论】:

这能回答你的问题吗? React - how do I unit test an API call in Jest? 【参考方案1】:

由于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'));

【讨论】:

你好@Estus,谢谢你的帮助。实施后我收到此错误。 TypeError: Cannot destructure property 'name' of '(0 , _useFetch.default)(...)' as it is undefined. 我更新了代码,响应应该有data属性。如果您没有删除jest.mock,请考虑这样做,它也可能导致此错误。 它解决了我上面提到的问题。是的,我也删除了 jest.mock 并将 waitFor 更改为 waitForElement 。我从这一行 await waitFor(() =&gt; expect(getByTestId('company-name').textContent).toMatch('Google')); 收到我在问题上发布的错误。 Here 是我的要点。你能告诉我我哪里做错了吗? 我明白了。 waitFor 和 waitForElement 是不同的函数。 waitForElement 需要一个选择器,它不应该在这里工作,它也被弃用了。 当我在@testing-library 挖掘node_modules 时有wait-for-element.js 。每当我使用 waitFor 时,它都会显示错误:TypeError: (0 , _react2.waitFor) is not a function。要使其与 waitForElement 一起使用,我必须进行哪些更改?

以上是关于如何在反应中使用 API 调用测试组件?的主要内容,如果未能解决你的问题,请参考以下文章

如何模拟在使用 Jest 测试的 React 组件中进行的 API 调用

如何在useEffect中使用axios请求测试反应组件?

如何使用反应在离子框架中调用api?

如何使用新的反应路由器钩子测试组件?

如何从单个反应组件中调用多个 graphql 突变?

有没有办法在反应功能组件中只进行一次 api 调用?