unit-testing - run - jasmine html
Cómo probar una actualización de prop en el componente React (6)
¿Cuál es la forma correcta de realizar una prueba unitaria de una actualización del componente React?
Aquí está mi accesorio de prueba;
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);
});
});
Esto funciona bien y la prueba pasa, sin embargo, esto muestra un mensaje de advertencia de reacción
''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.''
Todo lo que quiero probar es la actualización de una propiedad, no crear una nueva instancia del elemento con una propiedad diferente. ¿Hay una mejor manera de hacer esta actualización de la propiedad?
Advertencia: esto no cambiará los accesorios.
Pero para mí, todo lo que quería era probar mi lógica en componentWillReceiveProps
. Así que llamo myComponent.componentWillReceiveProps(/*new props*/)
a myComponent.componentWillReceiveProps(/*new props*/)
.
No necesitaba / quiero probar que React invoca el método cuando cambian los accesorios, o que React establece accesorios cuando los accesorios cambian, solo que se activa alguna animación si los accesorios difieren de lo que se transmitió.
Aquí hay una solución que he estado usando que usa ReactDOM.render pero no se basa en el valor de retorno (en desuso) de la función. Utiliza la devolución de llamada (3er argumento para ReactDOM.render) en su lugar.
Configure jsdom si no prueba en el navegador:
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;
Prueba usando render de reac-dom con devolución de llamada asíncrona:
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();
});
});
Esta es una pregunta antigua, pero en caso de que alguien más se tropiece con esto, la siguiente configuración me ha funcionado muy bien:
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
});
Esto hace que React ejecute la actualización de componentes habitual.
Si vuelve a renderizar el elemento con diferentes elementos en el mismo nodo contenedor, se actualizará en lugar de volver a montarse. Ver React.render .
En su caso, debe usar ReactDOM.render
directamente en lugar de TestUtils.renderIntoDocument
. Lo último crea un nuevo nodo contenedor cada vez que se llama , y por lo tanto un nuevo componente también.
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);
});
Tanto TestUtils.renderIntoDocument
como ReactDOM.render
usan el valor devuelto de ReactDOM.render
. De acuerdo con los documentos React :
ReactDOM.render () actualmente devuelve una referencia a la instancia raíz de ReactComponent. Sin embargo, el uso de este valor de retorno es heredado y debe evitarse porque las versiones futuras de React pueden representar componentes de forma asíncrona en algunos casos. Si necesita una referencia a la instancia raíz de ReactComponent, la solución preferida es adjuntar una referencia de devolución de llamada al elemento raíz
¿Qué pasa si tomamos este consejo y hacemos algo como esto:
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);
});
Enzyme biblioteca de Enzyme de AirBnB proporciona una solución elegante a esta pregunta.
proporciona un método setProps, que se puede llamar en un envoltorio superficial o 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);
});