web-dev-qa-db-ja.com

Reactコンポーネントでプロップ更新をテストする方法

React component prop update。の単体テストの正しい方法は何ですか。

これが私のテストフィクスチャです。

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

これは正常に機能し、テストはパスしますが、反応警告メッセージが表示されます

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

私がテストしたいのは、異なるプロパティを持つ要素の新しいインスタンスを作成するのではなく、プロパティの更新だけです。このプロパティを更新するより良い方法はありますか?

38
Win

同じコンテナノードで異なる小道具を使用して要素を再レンダリングすると、再マウントではなく更新されます。 React.render を参照してください。

あなたの場合、ReactDOM.renderの代わりにTestUtils.renderIntoDocumentを直接使用する必要があります。後の 呼び出されるたびに新しいコンテナノードを作成する なので、新しいコンポーネントも作成されます。

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

AirBnBの Enzyme ライブラリは、この質問に対するエレガントなソリューションを提供します。

これは、浅いラッパーまたはjsdomラッパーで呼び出すことができるsetPropsメソッドを提供します。

    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

警告:これは実際には小道具を変更しません。

しかし、私にとっては、componentWillReceivePropsでロジックをテストするだけでした。したがって、私はmyComponent.componentWillReceiveProps(/*new props*/)を直接呼び出しています。

私は、React小道具が変わるとメソッドを呼び出す、またはReact小道具が変わると小道具を設定します。小道具が渡されたものと異なる場合にトリガーされます。

15

ReactDOM.renderを使用しているが、関数からの(非推奨の)戻り値に依存しない、私が使用しているソリューションを次に示します。代わりにコールバック(ReactDOM.renderの3番目の引数)を使用します。

ブラウザでテストしない場合は、jsdomをセットアップします。

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;

非同期コールバックで反応ドームレンダリングを使用してテストします。

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

これは古い質問ですが、他の誰かがこれに出くわした場合には、次のセットアップがうまく機能しました:

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

これにより、React=が通常のコンポーネント更新を実行します。

0
kidney

酵素を使用してコンポーネントをマウントし、それに小道具を追加できます。

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

TestUtils.renderIntoDocumentReactDOM.renderは、ReactDOM.renderからの戻り値を使用します。 React docs :によると

ReactDOM.render()は現在、ルートReactComponentインスタンスへの参照を返します。ただし、この戻り値の使用はレガシーであり、将来のバージョンのReact=はコンポーネントを非同期的にレンダリングする場合があるため、避ける必要があります。ルートReactComponentインスタンスへの参照が必要な場合、推奨ソリューションはルート要素にコールバック参照を添付します

このアドバイスを受けて、次のようなことをしたらどうなるでしょうか。

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