2017-08-01 1 views
1

Ich bin ein Jest/React Anfänger. In der it des Spaßes muss ich warten, bis alle Versprechen vor der tatsächlichen Prüfung ausgeführt haben.Wie testet man eine React-Komponente im Unit-Test, die nach dem Fetch rendert?

Mein Code ist ähnlich wie folgt aus:

export class MyComponent extends Component { 
    constructor(props) { 
     super(props); 
     this.state = { /* Some state */ }; 
    } 

    componentDidMount() { 
     fetch(some_url) 
      .then(response => response.json()) 
      .then(json => this.setState(some_state); 
    } 

    render() { 
     // Do some rendering based on the state 
    } 
} 

Wenn die Komponente montiert ist, render() läuft zweimal: einmal nach dem Konstruktor läuft, und einmal nach fetch() (in componentDidMount()) beendet und die verkettete Versprechen fertig Ausführung) .

Mein Test-Code ist ähnlich wie folgt aus:

describe('MyComponent',() => { 

    fetchMock.get('*', some_response); 

    it('renders something',() => { 
     let wrapper = mount(<MyComponent />); 
     expect(wrapper.find(...)).to.have.something(); 
    }; 
} 

Was ich von it zurückkehren, läuft es nach dem ersten Mal render() ausführt, aber vor dem zweiten Mal. Wenn ich zum Beispiel fetchMock.flush().then(() => expect(...)) zurückgebe, wird das zurückgegebene Versprechen vor dem zweiten Aufruf an render() ausgeführt (ich glaube, ich kann verstehen, warum).

Wie kann ich warten, bis das zweite Mal render() vor dem Ausführen expect() aufgerufen wird?

+1

Es klingt für mich wie Sie versuchen, zu viele Dinge in einem Test zu testen.Was Sie testen möchten, ist, dass Ihre Fetch-Funktion aufgerufen wird, wenn die Komponente geladen wird, dann haben Sie mehrere andere Tests, bei denen der Status explizit an sie übergeben wurde, und Sie können überprüfen, ob die Komponente ordnungsgemäß gerendert wird. –

+0

@MattWatson Wenn ich (1) überprüfe, dass die Fetch-Funktion aufgerufen wird und (2) dass die Übergabe des Status ordnungsgemäß gerendert wird, konnte ich nicht überprüfen, dass (1.5) der Status richtig gesetzt wird. Wie überprüfe ich, ob der Status richtig eingestellt wurde? –

Antwort

0

fand ich eine Art und Weise zu tun, was ich ursprünglich gefragt . Ich habe (noch) keine Meinung, ob es eine gute Strategie ist oder nicht (in der Tat musste ich die Komponente unmittelbar danach umgestalten, daher ist diese Frage für das, was ich mache, nicht länger relevant). Wie auch immer, hier ist der Test-Code (Erklärung unten):

import React from 'react'; 
import { mount } from 'enzyme'; 
import { MyComponent } from 'wherever'; 
import fetchMock from 'fetch-mock'; 

let _resolveHoldingPromise = false; 

class WrappedMyComponent extends MyComponent { 

    render() { 
     const result = super.render(); 
     _resolveHoldingPromise && _resolveHoldingPromise(); 
     _resolveHoldingPromise = false; 
     return result; 
    } 

    static waitUntilRender() { 
     // Create a promise that can be manually resolved 
     let _holdingPromise = new Promise(resolve => 
      _resolveHoldingPromise = resolve); 

     // Return a promise that will resolve when the component renders 
     return Promise.all([_holdingPromise]); 
    } 
} 

describe('MyComponent',() => { 

    fetchMock.get('*', 'some_response'); 

    const onError =() => { throw 'Internal test error'; }; 

    it('renders MyComponent appropriately', done => { 
     let component = <WrappedMyComponent />; 
     let wrapper = mount(component); 
     WrappedMyComponent.waitUntilRender().then(
      () => { 
       expect(wrapper.find('whatever')).toBe('whatever'); 
       done(); 
      }, 
      onError); 
    }); 
}); 

Die Hauptidee ist, dass im Testcode, ich habe die Komponente Unterklasse (wenn diese Python war würde ich wahrscheinlich Affen patchen es, was funktioniert mehr oder weniger die gleiche Weise in diesem Fall), so dass seine render() Methode sendet ein Signal, dass es ausgeführt wird. Der Weg, um das Signal zu senden, besteht darin, ein Versprechen manuell zu lösen. Wenn ein Versprechen erstellt wird, erstellt es zwei Funktionen, lösen und ablehnen, die, wenn sie aufgerufen werden, das Versprechen beenden. Die Möglichkeit, Code außerhalb der Versprechen zu haben, löst das Versprechen, indem das Versprechen einen Verweis auf seine Auflösungsfunktion in einer externen Variablen speichert.

Danke an Fetch-Mock-Autor Rhys Evans, der mir freundlicherweise den manuell lösbaren Versprechen-Trick erklärt hat.

1

Ich würde Bedenken trennen, vor allem, weil einfacher zu warten und zu testen ist. Anstatt den Abruf innerhalb der Komponente zu deklarieren, würde ich es woanders machen, zum Beispiel in einer Redux-Aktion (wenn redux verwendet wird).

Testen Sie dann einzeln den Abruf und die Komponente, nach all dem ist Unit Testing.

Bei asynchronen Tests können Sie den Parameter done für den Test verwenden. Zum Beispiel:

Sie können Ihre Komponente testen, indem Sie einfach die geladenen Daten in die Requisiten senden.

describe('MyComponent',() => { 

    it('renders something',() => { 
    const mockResponse = { some: 'data' }; 
    let wrapper = mount(<MyComponent data={mockResponse}/>); 

    expect(wrapper.find(...)).to.have.something(); 
    }); 
}); 

Wenn es darum geht zu testen Sie es einfach zu halten brauchen, wenn Ihre Komponente schwer zu testen, dann ist etwas falsch mit Ihrem Design gibt es;)

+0

Ist es möglich, es so zu machen, wie ich es ursprünglich beabsichtigt hatte? Ich frage, um asynchrone Programmierung besser in den Griff zu bekommen. –

+0

Jest ist für Unit-Tests, was Sie versuchen, ist nicht Unit-Tests. Davon abgesehen können Sie den Status manuell aus Ihrem Test setzen, zum Beispiel 'wrapper.setState ({irgendein_status})' '' expect (wrapper.find (...)). To.have.etwas(); '. Um den Abruf zu testen ... gibt es hier nicht viele Möglichkeiten, außer es von der Komponente wegzunehmen;) – Crysfel

Verwandte Themen