2016-07-11 42 views
8

tutti.Reatt Enzima - Test `componentDidMount` Async Call

Sto riscontrando strani problemi con il test di un aggiornamento di stato dopo una chiamata asincrona in corso componentDidMount.

Ecco il mio codice componente:

'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; 

Ora, quello che sto facendo nel mio file di prova (Ho una configurazione composta da Mocha + Chai + Sinon, tutti al lavoro):

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

Lo stato non viene aggiornato, anche se chiamo update() sul wrapper. La lunghezza è ancora 0. Mi manca qualcosa qui? Devo prendere in giro la risposta del server in un altro modo?

Thnx per l'aiuto!

risposta

1

Ho dato un'occhiata a https://www.npmjs.com/package/request e ho capito che il parametro "body" manca dal callback.

Dovrebbe apparire come

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

Effettivamente. Ho sicuramente avuto un problema con questo, ma non ha risolto il vero problema. Quindi alla fine l'ho risolto. Ho scoperto che dovevo controllare setState in un timeout a causa della sua natura asincrona. Implementazione finale qui - https://github.com/r31gN/tdd-react-enzyme/blob/master/tests/tests.full.js (Sono anche passato a 'superagent', solo preferenze personali). –

+2

Utilizzato anche 'nock' per deridere la risposta del server. Aveva strani problemi con il falso server in sinon. La richiesta non è stata scoperta. Potrei dover scavare più a fondo in questo (forse stavo facendo cose strane lì). –

9

Puoi astratto l'elenco degli utenti di recupero lontano dal componente reagire tramite il passaggio di una funzione che restituisce una promessa modo che invece di

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

sostituirlo con

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

E all'interno del test di simulazione che la funzione :

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

Nota che ho fatto e funziona per me (anche senza la parte wrapper.update()) e qui ho cercato di applicare al vostro esempio di codice senza correre esso ..

anche si noti che dovrebbe funzionare anche in casi diversi da componentDidMount - come avere un'azione asincrona attivata dopo aver fatto clic su un pulsante, ad esempio ..