web-dev-qa-db-fra.com

Comment tester une mise à jour de prop sur React composant

Quelle est la bonne manière de tester l’unité d’une React composant prop update.

Voici mon appareil de test;

describe('updating the value', function(){
        var component;
        beforeEach(function(){
            component = TestUtils.renderIntoDocument(<MyComponent value={true} />);
        });

        it('should update the state of the component when the value prop is changed', function(){
            // Act
            component.props.value = false;
            component.forceUpdate();
            // Assert
            expect(component.state.value).toBe(false);
        });
});

Cela fonctionne bien et le test réussit, mais un message d’avertissement de réaction s’affiche.

'Warning: Dont set .props.value of the React component <exports />. Instead specify the correct value when initially creating the element or use React.cloneElement to make a new element with updated props.'

Tout ce que je veux tester, c'est la mise à jour d'une propriété, pas pour créer une nouvelle instance de l'élément avec une propriété différente. Y a-t-il une meilleure façon de faire cette mise à jour de propriété?

38
Win

Si vous refaites le rendu de l'élément avec des propriétés différentes dans le même nœud de conteneur, il sera mis à jour au lieu d'être remonté. Voir React.render .

Dans votre cas, vous devriez utiliser ReactDOM.render directement au lieu de TestUtils.renderIntoDocument. Le dernier crée un nouveau noeud de conteneur chaque fois qu'il est appelé , et donc un nouveau composant également.

var node, component;
beforeEach(function(){
    node = document.createElement('div');
    component = ReactDOM.render(<MyComponent value={true} />, node);
});

it('should update the state of the component when the value prop is changed', function(){
    // `component` will be updated instead of remounted
    ReactDOM.render(<MyComponent value={false} />, node);
    // Assert that `component` has updated its state in response to a prop change
    expect(component.state.value).toBe(false);
});
49

La bibliothèque Enzyme d'AirBnB fournit une solution élégante à cette question.

il fournit une méthode setProps, qui peut être appelée sur un wrapper peu profond ou jsdom.

    it("Component should call componentWillReceiveProps on update", () => {
        const spy = sinon.spy(Component.prototype, "componentWillReceiveProps");
        const wrapper = shallow(<Component {...props} />);

        expect(spy.calledOnce).to.equal(false);
        wrapper.setProps({ prop: 2 });
        expect(spy.calledOnce).to.equal(true);
    });
54
user1095118

Mise en garde: cela ne changera pas réellement les accessoires.

Mais pour moi, tout ce que je voulais, c’était tester ma logique dans componentWillReceiveProps. Donc, j'appelle directement myComponent.componentWillReceiveProps(/*new props*/).

Je n'avais pas besoin/je voulais tester que React appelle la méthode lorsque les accessoires changent, ou que React définit les accessoires lorsque ceux-ci changent, il suffit de voir une animation déclenché si les accessoires diffèrent de ce qui a été transmis.

15
David Gilbertson

Voici une solution que j'utilise qui utilise ReactDOM.render mais ne repose pas sur la valeur de retour (obsolète) de la fonction. Il utilise le callback (3ème argument de ReactDOM.render) à la place.

Configurez jsdom si vous n’effectuez pas de test dans le navigateur:

var jsdom = require('jsdom').jsdom;
var document = jsdom('<!doctype html><html><body><div id="test-div"></div></body></html>');
global.document = document;
global.window = doc.defaultView;

Testez en utilisant le rendu de réact-dom avec le rappel asynchrone:

var node, component;
beforeEach(function(done){
    node = document.getElementById('test-div')
    ReactDOM.render(<MyComponent value={true} />, node, function() {
        component = this;
        done();
    });
});

it('should update the state of the component when the value prop is changed', function(done){
    // `component` will be updated instead of remounted
    ReactDOM.render(<MyComponent value={false} />, node, function() {
        component = this;
        // Assert that `component` has updated its state in response to a prop change
        expect(component.state.value).toBe(false);
        done();
    });
});
1
Jon Harris

C'est une question plus ancienne, mais au cas où quelqu'un d'autre tomberait dessus, la configuration suivante a bien fonctionné pour moi:

it('updates component on property update', () => {
    let TestParent = React.createClass({
        getInitialState() {
            return {value: true};
        },
        render() {
            return <MyComponent value={this.state.value}/>;
        }
    });
    component = TestUtils.renderIntoDocument(<TestParent/>);
    component.setState({value: false});
    // Verification code follows
});

Cela fait React exécuter la mise à jour habituelle des composants.

0
kidney

Vous pouvez utiliser enzyme pour monter des composants et y ajouter des accessoires:

import React form 'react';
import component;
import {configure, mount} form 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import {expect} from 'chai';

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

describe('Testing component', () => {
  let wrapper;
  beforeEach(() => {
    component = mount(<MyComponent value={false} />);
  });
  it('should update the state of the component when the value prop is changed', function(){

    expect(component.props().children.props.value).toBe(false);
});
0
aqteifan

Tous les deux TestUtils.renderIntoDocument et ReactDOM.render utilise la valeur renvoyée par ReactDOM.render. Selon documents React :

ReactDOM.render () renvoie actuellement une référence à l'occurrence de ReactComponent. Toutefois, l'utilisation de cette valeur renvoyée est héritée et doit être évitée car les versions futures de React peuvent rendre les composants de manière asynchrone dans certains cas. Si vous avez besoin d'une référence à l'instance racine de ReactComponent, la solution privilégiée consiste à: attache une référence de rappel à l'élément racine

Et si nous prenons ce conseil et faisons quelque chose comme ceci:

let component, node;

const renderComponent = (props = {}) => {
  ReactDOM.render(<MyComponent ref={r => component = r} {...props} />, node);
}

beforeEach(function(){
    node = document.createElement('div');
    renderComponent({value: true}, node); 
});

it('should update the state of the component when the value prop is changed', function(){
    // `component` will be updated instead of remounted
    renderComponent({value: false}, node); 
    // Assert that `component` has updated its state in response to a prop change
    expect(component.state.value).toBe(false);
});
0
Yaniv Efraim