2016-07-11 5 views
8

все.React Enzyme - Test `componentDidMount` Async Call

У меня возникли странные проблемы с проверкой состояния обновления после асинхронного вызова в 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; 

Теперь, что я делаю в моих тестовых файлов (я имею установку, состоящую из Мокко + Chai + Синоне, все работает):

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. Я что-то пропустил? Нужно ли мне высмеивать ответ сервера другим способом?

Thnx за помощью!

ответ

1

Я посмотрел на https://www.npmjs.com/package/request и понял, что параметр «тело» отсутствует в обратном вызове.

Это должно выглядеть

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

Действительно. У меня определенно была проблема с этим, но это не решило настоящую проблему. Поэтому я в конце концов это выработал. Оказывается, мне нужно было проверить SetState в тайм-ауте из-за его асинхронного характера. Конечная реализация здесь - https://github.com/r31gN/tdd-react-enzyme/blob/master/tests/tests.full.js (я также переключился на «суперагент», только личные предпочтения). –

+2

Также используется 'nock', чтобы высмеять ответ сервера. Были странные проблемы с поддельным сервером в силоне. Запрос не попался. Мне, возможно, придется углубиться в это (возможно, я делал там какие-то напуганные вещи). –

9

Вы можешь абстрагировать поиск списка пользователей от среагировать компонент через пропускание функции, которая возвращает обещание так, что вместо

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

И внутри вашего теста издеваются, что Функция:

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, - например, при активации асинхронного действия после нажатия кнопки, например.