2017-11-09 11 views
0

Ich habe eine einfache React Parent-Komponente, die einen einzelnen Statuswert namens Counter hat. Unter der Parent-Komponente habe ich dann eine einzelne untergeordnete Komponente, die eine Schaltfläche mit einem OnClick-Handler ist, die an den Elternknoten zurückruft, um den Status zu aktualisieren.Test Reagieren mit Jest Problem mit Async-Aufruf

import React, { Component } from 'react'; 
import axios from 'axios' 

class Parent extends Component { 
    state = {counter: 0} 

    onClick =() => { 
    console.log("Button clicked") 
    this.setState(prevState => ({ 
     counter: prevState.counter + 1 
    })) 
    } 

    render() { 
    return (
     <div> 
     <Child onClick={this.onClick} action="Click"/> 
     </div> 
    ); 
    } 
} 

class Child extends Component { 

    render() { 
    return (
     <button onClick={this.props.onClick}>{this.props.action}</button> 
    ) 
    } 
} 

export default Parent 

Testet Fein Jest mit folgendem Test:

import React from 'react'; 
import {mount, shallow} from 'enzyme'; 
import Parent from '../Problem'; 

it('handles Click event',() => { 

    const wrapper = mount(<Parent />) 
    expect(wrapper.state().counter).toEqual(0) 
    console.log(wrapper.debug()) 
    const button = wrapper.find('button').at(0); 
    button.simulate('click'); 
    expect(wrapper.state().counter).toEqual(1) 
    console.log(wrapper.state().counter) 

}); 

Wenn ich den Code zum einem Asynchron-Update folgenden ändern zu simulieren:

import React, { Component } from 'react'; 
import axios from 'axios' 

class Parent extends Component { 
    state = {counter: 0} 

    onClickAsync =() => { 
    console.log("Async Button clicked") 
    axios.get(`https://api.github.com/users/smitp33`) 
     .then(resp => { 
      this.setState(prevState => ({ 
      counter: prevState.counter + 1 
      })) 
     } 
    ) 
    } 

    render() { 
    return (
     <div> 
     <Child onClick={this.onClickAsync} action="Click"/> 
     </div> 
    ); 
    } 
} 

class Child extends Component { 

    render() { 
    return (
     <button onClick={this.props.onClick}>{this.props.action}</button> 
    ) 
    } 
} 

export default Parent 

... dann der Derselbe Test schlägt fehl, da der Zähler nicht aktualisiert wurde.

handles Click event 

    expect(received).toEqual(expected) 

    Expected value to equal: 
     1 
    Received: 
     0 

     at Object.<anonymous>.it (src/__tests__/Problem.test.js:14:35) 
     at Promise.resolve.then.el (node_modules/p-map/index.js:46:16) 
     at process._tickCallback (internal/process/next_tick.js:109:7) 

Ich habe einen Rückruf an den setState Funktion hinzugefügt um zu beweisen, dass der Staat in der Tat hat sich verändert, und es hat auf jeden Fall, so dass ich glaube, dass die Frage um den Jest-Test ist einfach abgeschlossen, bevor der Asynchron-Komponente Aufruf abgeschlossen ist.

Ich habe verschiedene Artikel und die Jest Docs gelesen und versucht, alle möglichen Dinge hier zu versuchen und den Test zu bestehen, aber bisher nicht gelungen, also dachte ich, ich würde hier posten, um zu sehen, ob jemand etwas bieten könnte Klare Ratschläge, wie genau das angegangen werden sollte.

Details zu genauen Abhängigkeiten sind wie folgt:

"dependencies": { 
    "axios": "^0.17.0", 
    "enzyme": "^3.1.0", 
    "enzyme-adapter-react-16": "^1.0.2", 
    "jest-enzyme": "^4.0.1", 
    "prop-types": "^15.6.0", 
    "react": "^16.0.0", 
    "react-circular-progressbar": "^0.5.0", 
    "react-dom": "^16.0.0", 
    "react-percentage-circle": "^1.1.3", 
    "react-scripts": "1.0.15" 
    } 

Das alles in einem Vanille 'erstellen reagieren-App' Setup mit der in den setupTests.js definiert Folgendes geschieht:

import Enzyme from "enzyme"; 
import 'jest-enzyme'; 
import Adapter from "enzyme-adapter-react-16"; 

Enzyme.configure({ adapter: new Adapter() }); 

global.requestAnimationFrame = function(callback) { 
    setTimeout(callback, 0); 
}; 

Antwort

0

this.setState verhält sich asynchron. Daher bedeutet die Festlegung des Status nicht, dass es sofort aktualisiert wird. In Ihrer Funktion async, versuchen Sie dies eher:

onClickAsync =() => { 
    console.log("Async Button clicked") 
    axios.get(`https://api.github.com/users/smitp33`) 
    .then(resp => { 
     this.setState({ 
     counter: this.state.counter + 1; 
     },() => { 
     console.log(this.state.counter); 
     }) 
    } 
    ) 
} 

das gesagt ist, in Ihren Tests, warten Sie auf den Asynchron-Betrieb, bevor sie irgendwelche Aussagen zu beenden. Verwenden Sie also die .then()-Methode, mit der Sie die Assertions tatsächlich ausführen.

+0

dank fshock - ich wusste, dass der Anruf zu setState nicht sofort ist daher hatte ich mit einem Rückruf genau getestet, wie Sie angegeben. Interessanterweise sehen Sie, wenn Sie den Code normal ausführen, die Ausgabe von console.log, wenn Sie innerhalb von Jest ausgeführt werden; o).Mit Ihrem Hinweis oben habe ich festgestellt, dass sie sich hier auf Versprechungen verlassen müssen, mit denen Jest dann glücklich umgehen wird ... müssen Sie nur die Funktion ändern, um das eigentliche Versprechen zurückzugeben. – smitp33

+0

ja absolut. – brandNew

0

mit den Details über I bereitgestellt geschafft, dies an die Arbeit wie folgt:

Statt den async Klick im Rahmen einer Simulation zu testen ich die Funktion in Isolation Test durchführen möchten.

Durch die Änderung des Click-Ereignisses, um tatsächlich etwas zurückzugeben ... in diesem Fall eine Zusage war ich dann in der Lage, richtig Async zu verwenden und im Test warten.

Modified Code:

onClickAsync =() => { 
    console.log("Async Button clicked") 
    return (
     axios.get(`https://api.github.com/users/smitp33`) 
      .then(resp => { 
       this.setState(prevState => ({ 
       counter: prevState.counter + 1 
       })) 
      } 
     ) 
    ) 
    } 

Modified-Test:

test('calls Async Click function', async() => { 
    const wrapper = mount(<Parent />) 
    console.log(wrapper.state().counter) 
    await wrapper.instance().onClickAsync() 
    console.log(wrapper.state().counter) 
    expect(wrapper.state().counter).toEqual(1) 
});