2017-08-22 25 views
7

W mojej aplikacji do tworzenia-reakcji próbuję przetestować komponent, który wykonuje wiele setState s po zamontowaniu.Jak poczekać, aż składnik React zakończy całkowicie aktualizację w Jest i/lub Enzyme?

class MyComponent extends React.Component { 

    state = { 
    a: undefined, 
    b: undefined, 
    c: undefined, 
    }; 

    fetchA() { 
    // returns a promise 
    } 

    fetchB() { 
    // returns a promise 
    } 

    fetchC() { 
    // returns a promise 
    } 

    async componentDidMount() { 
    const a = await fetchA(); 
    this.setState({ a }); 
    } 

    async componentDidUpdate(prevProps, prevState) { 
    if (prevState.a !== this.state.a) { 
     const [b, c] = await Promise.all([ 
     this.fetchB(a); 
     this.fetchC(a); 
     ]); 
     this.setState({ b, c }); 
    } 
    } 

    ... 

} 

W moim teście zrobić coś takiego, próbując niech setState w componentDidUpdate zakończyć przed dokonaniem twierdzeń.

import { mount } from 'enzyme'; 

describe('MyComponent',() => { 

    const fakeA = Promise.resolve('a'); 
    const fakeB = Promise.resolve('b'); 
    const fakeC = Promise.resolve('c'); 

    MyComponent.prototype.fetchA = jest.fn(() => fakeA); 
    MyComponent.prototype.fetchB = jest.fn(() => fakeB); 
    MyComponent.prototype.fetchC = jest.fn(() => fakeC); 

    it('sets needed state', async() => { 
    const wrapper = mount(<MyComponent />); 
    await Promise.all([ fakeA, fakeB, fakeC ]); 
    expect(wrapper.state()).toEqual({ 
     a: 'a', 
     b: 'b', 
     c: 'c', 
    }); 
    }); 

}); 

Oto interesująca część: Moje badania nad zawiedzie ponieważ ostatni setState wezwanie (w componentDidUpdate) nie zostało zakończone, gdy twierdzenie jest wykonany. W tym momencie ustawiono state.a, ale state.b i state.c nie zostało jeszcze ustawione.

Jedynym sposobem, w jaki mogę to zrobić, jest zaklinowanie await Promise.resolve(null) tuż przed stwierdzeniem, aby dać ostatnie setState dodatkowe cykle/cykle do ukończenia. To wygląda na zbyt hacky.

Inną rzeczą, którą próbowałem, jest owijanie asercji w setImmediate(), która działa dobrze, dopóki przechodzi twierdzenie. Jeśli się nie powiedzie, zakończy cały test z powodu nieprzechwyconego błędu.

Czy ktoś poradził sobie z tym problemem?

+0

Czy to zadziała, jeśli zastąpisz instrukcję 'Promise.all' dwiema instrukcjami, np.' Const b = czeka na this.fetchB (a) ... '? A może udawać "Promise.all". Domyślam się, że problemem jest obietnica, którą tam stworzysz, o której nie wiesz. –

+0

Czy odnosisz się do 'Promise.all' w komponencie lub w teście? Jeśli jest w teście, jestem prawie pewien, że potwierdzam, że żart czeka, aż wszystkie trzy dobiegną końca. Jednak test wznawia się wkrótce, gdy trzy kończą się, nie czekając na zakończenie ostatniego 'setState' w' componentDidUpdate'. Niezależnie od tego, spróbuję zagrać z tym, co powiedziałeś. – vleong

+0

Czy rozwiązałeś ten problem? – prime

Odpowiedz

2

Tak to rozwiązałem. Mam nadzieję, że pomaga komuś.

import { mount } from 'enzyme'; 

describe('MyComponent',() => { 

    const fakeA = Promise.resolve('a'); 
    const fakeB = Promise.resolve('b'); 
    const fakeC = Promise.resolve('c'); 

    MyComponent.prototype.fetchA = jest.fn(() => fakeA); 
    MyComponent.prototype.fetchB = jest.fn(() => fakeB); 
    MyComponent.prototype.fetchC = jest.fn(() => fakeC); 

    it('sets needed state', async (done) => { 
    const wrapper = mount(<MyComponent />); 
    await Promise.all([ fakeA, fakeB, fakeC ]); 

    setImmediate(() => { 
     // Without the try catch, failed expect will cause the 
     // whole test to crash out. 
     try { 
     expect(wrapper.state()).toEqual({ 
      a: 'a', 
      b: 'b', 
      c: 'c', 
     }); 
     } catch(error) { 
     done.fail(error); 
     } 
     done(); 

    }); 

}); 
Powiązane problemy