2015-06-03 10 views
29

Jaki jest prawidłowy sposób testowania jednostki w przypadku aktualizacji komponentu React komponentu.Jak przetestować aktualizację podpory dotyczącej komponentu React

Oto mój test urządzenia;

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

To działa prawidłowo, a test przechodzi, jednak wyświetla komunikat reagować ostrzeżenie

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

wszystkim chcę przetestować jest aktualizacja nieruchomości, aby nie tworzyć nowe wystąpienie elementu z inną własnością. Czy istnieje lepszy sposób na wykonanie tej aktualizacji właściwości?

Odpowiedz

39

Jeśli ponownie wyświetlisz element z różnymi rekwizytami w tym samym węźle kontenera, zostanie on zaktualizowany zamiast ponownie zamontowany. Zobacz React.render.

W twoim przypadku powinieneś użyć ReactDOM.render bezpośrednio zamiast TestUtils.renderIntoDocument. Później creates a new container node every time it is called, a więc także nowy składnik.

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

W praktyce tynku nie nazywa się wyraźnie we wszystkich przypadkach. Lepszym sposobem jest użycie zmiany danych stanu w celu wyzwolenia ponownego renderowania, ponieważ mogą istnieć inne haki w środowisku na żywo, których brakuje, bezpośrednio wywołując zmianę stanu. – arkoak

+2

Jeśli testujesz odpowiedź na zmiany rekwizytów w coś podobnego do 'componentWillReceiveProps' lub' componentDidUpdate', itd., To tak też bym to zrobił. Powiedziałem, że spróbuję przepisać komponent tak, aby wartość w stanie była obliczana dynamicznie w czasie renderowania, jeśli to możliwe (zamiast użycia stanu). –

+4

+1 za odpowiedź. Zauważ, że to teraz 'ReactDOM.render' zamiast tylko' React.render' (od wersji 0.14). – jrubins

12

Zastrzeżenie: to nie będzie faktycznie zmienić rekwizyty.

Ale dla mnie wszystko, czego chciałem, to przetestować moją logikę w componentWillReceiveProps. Dzwonię więc bezpośrednio pod numer myComponent.componentWillReceiveProps(/*new props*/).

nie muszę/chcę przetestować, które reagują wywołuje metodę, gdy zmieni rekwizyty, które reagują lub zestawy rekwizyty podczas rekwizyty zmienić, tak, że niektóre animacja jest wyzwalany jeśli rekwizyty różnią się do tego, co zostało przekazane w.

48

Biblioteka AirBnB Enzyme zapewnia i eleganckie rozwiązanie tego pytania.

zapewnia metodę setProps, którą można wywołać na płytkiej lub jsdomowej owijce.

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

Jest to starsza pytanie, ale w przypadku ktokolwiek inny natyka się na to, co następuje konfiguracja pracował dla mnie ładnie:

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

To sprawia React uruchomić zwykłą aktualizację składnika.

0

Zarówno TestUtils.renderIntoDocument, jak i ReactDOM.render używa zwracanej wartości z ReactDOM.render. Według React docs:

ReactDOM.render() obecnie zwraca odwołanie do głównej instancji ReactComponent. Jednak użycie tej wartości zwracanej jest starszą wersją i należy jej unikać, ponieważ przyszłe wersje React mogą w niektórych przypadkach wyrenderować komponenty asynchronicznie. Jeśli potrzebujesz odniesienie do korzeni ReactComponent przykład, preferowanym rozwiązaniem jest dołączenie ref oddzwonienia do elementu głównego

Co jeśli weźmiemy ten doradzić i zrobić coś takiego:

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

Oto rozwiązanie, z którego korzystałem, używa ReactDOM.render, ale nie opiera się na (przestarzałej) wartości zwracanej przez funkcję. Zamiast tego używa wywołania zwrotnego (trzeci argument do ReactDOM.render).

Konfiguracja jsdom jeśli nie testuje w przeglądarce:

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; 

Test z zastosowaniem reagować-dom render z asynchronicznym zwrotnego:

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(); 
    }); 
}); 
Powiązane problemy