2015-06-03 51 views
29

單元測試React組件prop更新的正確方法是什麼?如何測試React組件上的prop更新

這是我的測試夾具;

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

所有我想要測試是一個屬性的更新,而不是爲了創建元素的新實例與不同的財產。有沒有更好的方法來做這個屬性更新?

回答

39

如果在同一個容器節點中使用不同的道具重新渲染元素,它將被更新而不是重新安裝。見React.render

對於您的情況,您應該直接使用ReactDOM.render而不是TestUtils.renderIntoDocument。後者creates a new container node every time it is called,因此也是一個新的組件。

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

在實際操作中,渲染是不是在所有情況下明確要求。更好的方法是使用狀態數據更改來觸發重新呈現,因爲在直播環境中可能會有一些其他鉤子,這些鉤子會通過直接調用狀態更改而錯過。 – arkoak

+2

如果您正在測試對某些東西(如「componentWillReceiveProps」或「componentDidUpdate」等)的支持更改的響應,這也是我的做法。也就是說,我會嘗試重寫組件,以便在渲染時動態計算狀態值,如果可能的話(而不是使用狀態)。 –

+4

+1的答案。請注意,現在是'ReactDOM.render'而不是'React.render'(我認爲版本爲0.14)。 – jrubins

12

警告:這不會真正改變的道具。

但對於我來說,我只想要在componentWillReceiveProps中測試我的邏輯。所以我直接撥打myComponent.componentWillReceiveProps(/*new props*/)

我並不需要/想測試陣營調用方法時的道具改變,或作出反應套道具道具時改變,只是,如果道具不同,什麼是在通過一些動畫被觸發。

48

AirBnB的Enzyme庫爲這個問題提供了優雅的解決方案。

它提供了一個setProps方法,可以在淺層或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); 
    }); 
0

這是一個老問題,但在在這種情況下,任何人絆倒,以下設置爲我工作很好:

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

這使得運行做出反應通常組件更新。

0

TestUtils.renderIntoDocumentReactDOM.render均使用從ReactDOM.render返回的值。根據React docs

ReactDOM.render()當前返回對根ReactComponent實例的引用。但是,使用此返回值是遺留的,應該避免,因爲在某些情況下,未來版本的React可能會異步渲染組件。如果您需要在根ReactComponent實例的引用,首選的解決方案是將回調REF附加到根元素

如果我們藉此提醒,做這樣的事情:

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

這裏的我一直在使用的解決方案使用ReactDOM.render,但不依賴函數的(不推薦)返回值。它使用回調(ReactDOM.render的第三個參數)。

設置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; 

測試使用反應-DOM渲染異步回調:

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(); 
    }); 
}); 
相關問題