2016-07-11 10 views
8

alle.React Enzyme - Testen Sie `componentDidMount` Async Call

Ich habe seltsame Probleme mit dem Testen einer Statusaktualisierung nach einem asynchronen Anruf in componentDidMount passiert.

Hier ist mein Komponentencode:

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

Nun, was ich in meinen Test-Dateien zu tun (ich habe ein Setup bestehend aus Mocha + Chai + Sinon, alle arbeiten):

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

State wird nicht aktualisiert, obwohl ich update() auf Wrapper aufrufen. Die Länge ist immer noch 0. Fehle ich hier etwas? Muss ich die Serverantwort auf andere Weise verspotten?

Thnx für die Hilfe!

Antwort

1

Ich habe mir https://www.npmjs.com/package/request angesehen und herausgefunden, dass der 'body' Parameter im Callback fehlt.

Es sollte wie folgt aussehen

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

In der Tat. Ich hatte definitiv ein Problem damit, aber es hat das eigentliche Problem nicht gelöst. Also habe ich es irgendwann ausprobiert. Es stellte sich heraus, dass ich SetState aufgrund seiner asynchronen Natur in einem Timeout überprüfen musste. Endgültige Implementierung hier - https://github.com/r31gN/tdd-react-enzyme/blob/master/tests/tests.full.js (Ich wechselte auch zu "superagent", nur persönliche Vorliebe). –

+2

Wird auch 'nock' verwendet, um die Serverantwort nachzuahmen. Hatte seltsame Probleme mit dem falschen Server in Sinon. Anfrage wurde nicht erwischt. Ich muss mich vielleicht tiefer einarbeiten (vielleicht habe ich da ein paar abgefahrene Sachen gemacht). –

9

können Sie abstrakt die Benutzerliste Retrieval von der Komponente reagieren entfernt eine Funktion über vorbei, die ein Versprechen zurück, so dass anstelle von

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

Ersetzen Sie es mit

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

Und in Ihrem Test Mock, Funktion:

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

Bitte beachte, dass ich dies getan haben und es funktioniert für mich (auch ohne wrapper.update() Teil) und hier habe ich versucht, es zu Ihrem Codebeispiel anwenden, ohne sie läuft ..

Auch Beachten Sie, dass es auch in anderen Fällen als componentDidMount funktionieren sollte - etwa wenn nach dem Klicken auf eine Schaltfläche eine asynchrone Aktion ausgelöst wird.

Verwandte Themen