Как проверить обновление prop на компоненте React
Каков правильный способ модульного тестирования обновления поддержки компонента React.
Вот моя тестовая арматура:
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.'
Все, что я хочу проверить, - это обновление свойства, а не создание нового экземпляра элемента с другим свойством. Есть ли лучший способ сделать это обновление свойств?
Ответы
Ответ 1
Если вы повторно визуализируете элемент с разными реквизитами в одном контейнере node, он будет обновлен вместо повторной установки. См. React.render.
В вашем случае вы должны использовать ReactDOM.render
непосредственно вместо TestUtils.renderIntoDocument
. Позднее создает новый контейнер node каждый раз, когда он вызывается, и, следовательно, новый компонент тоже.
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);
});
Ответ 2
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);
});
Ответ 3
Предостережение: на самом деле это не повлияет на реквизиты.
Но для меня все, что я хотел, это проверить мою логику в componentWillReceiveProps
. Поэтому я вызываю myComponent.componentWillReceiveProps(/*new props*/)
напрямую.
Мне не нужно было/не хотеть тестировать, что React вызывает этот метод при изменении реквизита или что React устанавливает реквизит при изменении реквизита, только если какая-то анимация срабатывает, если реквизит отличается от того, что было передано.
Ответ 4
Это старый вопрос, но в случае, если кто-то еще наткнулся на это, следующая настройка работала для меня красиво:
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 запускать обычное обновление компонента.
Ответ 5
Оба TestUtils.renderIntoDocument
и ReactDOM.render
используют возвращаемое значение из ReactDOM.render
. Согласно React docs:
ReactDOM.render() теперь возвращает ссылку на экземпляр root ReactComponent. Однако использование этого возвращаемого значения является устаревшим и его следует избегать, поскольку будущие версии React могут в некоторых случаях подвергать компоненты асинхронно. Если вам нужна ссылка на экземпляр root 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);
});
Ответ 6
Здесь решение, которое я использовал, использует 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;
Тестирование с использованием рендеринга реагирования с асинхронным обратным вызовом:
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();
});
});