【问题标题】:React Enzyme - Test `componentDidMount` Async CallReact Enzyme - 测试 `componentDidMount` 异步调用
【发布时间】:2016-11-13 11:24:59
【问题描述】:

每个人。

componentDidMount 发生异步调用后,我在测试状态更新时遇到了奇怪的问题。

这是我的组件代码:

'use strict';


import React from 'react';
import UserComponent from './userComponent';
const request = require('request');


class UsersListComponent extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      usersList: []
    };
  }

  componentDidMount() {
    request('https://api.github.com/users', (err, res) => {
      if (!err && res.statusCode === 200) {
        this.setState({
          usersList: res.slice(0)
        });
      }
      else {
        console.log(err);
      }
    });
  }

  render() {
    if (!this.state.usersList.length) {
      return null;
    }

    return (
      <div className="users-list">
        { this._constructUsersList() }
      </div>
    );
  }

  _constructUsersList() {
    return this.state.usersList.map((user, index) => {
      return (
        <UserComponent
              key={ index }
              name={ user.name }
              age={ user.age } />
      );
    });
  }
};


export default UsersListComponent;

现在,我在测试文件中做了什么(我有一个由 Mocha + Chai + Sinon 组成的设置,一切正常):

import React from 'react';
import { expect } from 'chai';
import { shallow, mount, render } from 'enzyme';
import sinon from 'sinon';
import UsersListComponent from '../src/usersListComponent';


describe('Test suite for UsersListComponent', () => {
  it('Correctly updates the state after AJAX call in `componentDidMount` was made', () => {
    const server = sinon.fakeServer.create();
    server.respondWith('GET', 'https://api.github.com/users', [
      200,
      {
        'Content-Type': 'application/json',
        'Content-Length': 2
      },
      '[{ "name": "Reign", "age": 26 }]'
    ]);
    let wrapper = mount(<UsersListComponent />);
    server.respond();
    server.restore();
    expect(wrapper.update().state().usersList).to.be.instanceof(Array);
    console.log(wrapper.update().state().usersList.length);
  });
});

状态没有得到更新,即使我在包装器上调用update()。长度仍为 0。我在这里遗漏了什么吗?我需要以其他方式模拟服务器响应吗?

感谢您的帮助!

【问题讨论】:

    标签: javascript reactjs enzyme


    【解决方案1】:

    给你我的解决方案:

    • 酶 3.9.0
    • 酶适配器反应16 1.9.1
    • 反应 16.8.0
    • 开玩笑

    1- 模拟数据服务

    您必须模拟数据服务(通常类似于 axios 或其他 HTTP 库)。

    文件:__mocks__/LogDataService.js

    let searchData = {}
    
    const LogDataService = {
        search: () => new Promise((resolve, reject) =>
            process.nextTick(() => resolve(searchData))
        ),
        setSearchData: (data) => searchData = data
    }
    
    export default LogDataService
    

    2- 测试

    文件:__tests__/toto.test.js

    jest.mock('services/LogDataService')
    
    import LogDataService from 'services/LogDataService';
    
    // Declare a super promise that will synchro all promises
    function flushPromises() {
        return new Promise(resolve => setImmediate(resolve));
    }
    
    it('renders without data', async () => {
        // Set dummy data
        LogDataService.setSearchData([])
        // The component we want to test that use
        // LogDataService.search method we just mock up
        const component = mount(<JobsExecutionTimeline />);
        // wait
        await flushPromises();
        // re-build the render
        component.update()
        // Compare to snapshot
        expect(toJson(component)).toMatchSnapshot();
    });
    

    【讨论】:

    • 非常好,您可以将其缩短为await new Promise(setImmediate),如果您愿意,可以删除辅助函数。
    【解决方案2】:

    我查看了https://www.npmjs.com/package/request 并发现回调中缺少“body”参数。

    应该是这样的

    ...
    request('https://api.github.com/users', (err, res, body) => {
        if (!err && res.statusCode === 200) {
          this.setState({
            usersList: body.slice(0)
          });
        }
    ...
    

    【讨论】:

    • 确实如此。我肯定有这个问题,但它并没有解决真正的问题。所以我最终解决了这个问题。事实证明,由于 setState 的异步性质,我不得不在超时时检查它。最终实现在这里-github.com/r31gN/tdd-react-enzyme/blob/master/tests/…(我也切换到superagent,只是个人喜好)。
    • 还使用nock 模拟服务器响应。 sinon 的假服务器有奇怪的问题。请求没有被抓住。我可能需要深入挖掘(也许我在那里做了一些时髦的事情)。
    【解决方案3】:

    您可以通过传递返回 Promise 的函数将用户列表检索从反应组件中抽象出来,而不是

      componentDidMount() {
        request('https://api.github.com/users', (err, res) => {
          if (!err && res.statusCode === 200) {
            this.setState({
              usersList: res.slice(0)
            });
          }
          else {
            console.log(err);
          }
        });
      }
    

    替换为

      componentDidMount() {
         var comp = this;
         this.props.getUsers()
            .then(function(usersList) {
              comp.setState({
                usersList: usersList
              });
            })
            .catch(function (err) {
                console.log(err);
            });
      }
    

    在您的测试模拟中,getUsers 函数:

        it('Correctly updates the state after AJAX call in `componentDidMount` was made', (done) => {
    
          let resolveGetUsers;
    
          let getUsers = function() {
            return new Promise(function (resolve, reject) {
                      resolveGetUsers = resolve;
                    });
          }
    
          let wrapper = mount(<UsersListComponent getUsers={getUsers} />);
    
          resolveGetUsers([{ "name": "Reign", "age": 26 }]);
    
    
          // promise resolve happens in a subsequent event loop turn so move assertions inside setImmediate
          setImmediate(() => {
    
            expect(wrapper.update().state().usersList).to.be.instanceof(Array);
            ...
    
            done();
          });
        }
    

    请注意,我已经这样做了,它对我有用(即使没有 wrapper.update() 部分),在这里我尝试将它应用到您的代码示例而不运行它..

    另请注意,它也应该在 componentDidMount 以外的情况下工作 - 例如在单击按钮后触发异步操作..

    【讨论】:

    • 请注意,如果在beforeEach() 中使用它,setImmediate() 部分可能会导致错误,因为其内容的执行被延迟。您的测试可能会在 setImmediate() 块完成之前开始执行。为了解决这个问题,将setImmediate() 块包装在new Promise() 中,只有在块中的所有语句都完成后才能解决。使 beforeEach() 函数异步并在那里有一行:await new Promise((resolve, reject) =&gt; { /* setImmediateBlock goes here; don't forget to put resolve() at the end of it. */ });
    猜你喜欢
    • 2018-12-23
    • 2018-08-31
    • 2019-06-25
    • 2020-02-12
    • 2020-01-20
    • 2020-11-25
    • 1970-01-01
    • 1970-01-01
    • 2021-04-14
    相关资源
    最近更新 更多