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.'
私がテストしたいのは、異なるプロパティを持つ要素の新しいインスタンスを作成するのではなく、プロパティの更新だけです。このプロパティを更新するより良い方法はありますか?
同じコンテナノードで異なる小道具を使用して要素を再レンダリングすると、再マウントではなく更新されます。 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);
});
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);
});
警告:これは実際には小道具を変更しません。
しかし、私にとっては、componentWillReceiveProps
でロジックをテストするだけでした。したがって、私はmyComponent.componentWillReceiveProps(/*new props*/)
を直接呼び出しています。
私は、React小道具が変わるとメソッドを呼び出す、またはReact小道具が変わると小道具を設定します。小道具が渡されたものと異なる場合にトリガーされます。
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();
});
});
これは古い質問ですが、他の誰かがこれに出くわした場合には、次のセットアップがうまく機能しました:
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=が通常のコンポーネント更新を実行します。
酵素を使用してコンポーネントをマウントし、それに小道具を追加できます。
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);
});
TestUtils.renderIntoDocument
とReactDOM.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);
});