React Enzyme - Test `componentDidMount` Async Call

后端 未结 3 2026
迷失自我
迷失自我 2020-12-09 11:28

everyone.

I\'m having weird issues with testing a state update after an async call happening in componentDidMount.

Here\'s my component code:

相关标签:
3条回答
  • 2020-12-09 12:05

    You can abstract the user list retrieval away from the react component via passing a function that returns a Promise so that instead of

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

    Replace it with

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

    And inside your test mock that getUsers function:

        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();
          });
        }
    

    Note that I have done this and it works for me (even without the wrapper.update() part) and here I tried to apply it to your code example without running it..

    Also note that it should work in cases other then componentDidMount too - like having an async action triggered after clicking a button for example..

    0 讨论(0)
  • 2020-12-09 12:23

    Give you my solution:

    • enzyme 3.9.0
    • enzyme-adapter-react-16 1.9.1
    • react 16.8.0
    • jest

    1- Mock data service

    You must mock the data service (usually something like axios or an other HTTP library).

    File: __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

    File: __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();
    });
    
    0 讨论(0)
  • 2020-12-09 12:24

    I had a look at https://www.npmjs.com/package/request and figured out that the 'body' parameter is missing from the callback.

    It should look like

    ...
    request('https://api.github.com/users', (err, res, body) => {
        if (!err && res.statusCode === 200) {
          this.setState({
            usersList: body.slice(0)
          });
        }
    ...
    
    0 讨论(0)
提交回复
热议问题