2017-04-03 1 views
0

J'ai suivi les étapes de documentation pour tester l'épopée.Redux-observable: échec test jest pour épique

... 
store.dispatch({ type: FETCH_USER }); 

expect(store.getActions()).toEqual([ 
    { type: FETCH_USER }, 
    { type: FETCH_USER_FULFILLED, payload } 
]); 
... 

Mais je suis tombé en panne car une seconde action a été reçue plus tard comme suit.

Test failed 
    Expected value to equal: 
     [{"type": "FETCH_USER"}, {"type": "FETCH_USER_FULFILLED", "payload": [some]}] 
    Received: 
     [{"type": "FETCH_USER"}] 

    Difference: 

    - Expected 
    + Received 

    @@ -1,20 +1,5 @@ 
    Array [ 
     Object {"type": "FETCH_USER"}, 
     Object {"type": "FETCH_USER_FULFILLED", "payload": [some]} ] // this is what should be. 

Donc, je pense que je devrais savoir quand l'expédition est terminée ou d'autres comme ça. Comment puis-je résoudre ce problème?

J'utilisé fetch() et Rx.Observable.fromPromise au lieu de ajax.getJSON()

Voici mon épopée.

const fetchUserEpic = (action$) => 
    action$ 
    .ofType(FETCH_USER) 
    .mergeMap(() => { 
     return Rx.Observable.fromPromise(api.fetchUser()) 
     .map((users) => ({ 
      type: FETCH_USER_FULFILLED, 
      payload: { users } 
     })) 
     .catch((error) => Rx.Observable.of({ 
      type: FETCH_USER_ERROR, 
      payload: { error } 
     })) 
     .takeUntil(action$.ofType(FETCH_USER_CANCELLED)) 
    }) 

Répondre

4

La raison en est que les promesses sont toujours résolus sur le next microtask afin que votre api.fetchUser() n'émet pas de manière synchrone.

Vous devrez soit se moquer dehors, utilisez quelque chose comme Promise.resolve().then(() => expect(store.getActions).toEqual(...) attendre la prochaine Microtask, ou vous pouvez expérimenter avec tester vos épopées directement sans utiliser Redux. Ce sera notre histoire de test préférée dans les docs quand je (ou quelqu'un d'autre) a le temps de les écrire. Donc, au lieu d'utiliser redux et le middleware dans vos tests, nous appelons simplement la fonction épique directement avec nos propres simulacres. Beaucoup plus facile et propre.

Avec cette approche, nous pouvons tirer parti de la nouvelle fonctionnalité d'injection de dépendance de Redux observable: https://redux-observable.js.org/docs/recipes/InjectingDependenciesIntoEpics.html


import { createEpicMiddleware, combineEpics } from 'redux-observable'; 
import { ajax } from 'rxjs/observable/dom/ajax'; 
import rootEpic from './somewhere'; 

const epicMiddleware = createEpicMiddleware(rootEpic, { 
    dependencies: { getJSON: ajax.getJSON } 
}); 

// Notice the third argument is our injected dependencies! 
const fetchUserEpic = (action$, store, { getJSON }) => 
    action$.ofType('FETCH_USER') 
    .mergeMap(() => 
     getJSON(`/api/users/${payload}`) 
     .map(response => ({ 
      type: 'FETCH_USER_FULFILLED', 
      payload: response 
     })) 
    ); 

import { ActionsObservable } from 'redux-observable'; 
import { fetchUserEpic } from './somewhere/fetchUserEpic'; 

const mockResponse = { name: 'Bilbo Baggins' }; 
const action$ = ActionsObservable.of({ type: 'FETCH_USERS_REQUESTED' }); 
const store = null; // not needed for this epic 
const dependencies = { 
    getJSON: url => Observable.of(mockResponse) 
}; 

// Adapt this example to your test framework and specific use cases 
fetchUserEpic(action$, store, dependencies) 
    .toArray() // buffers all emitted actions until your Epic naturally completes() 
    .subscribe(actions => { 
    assertDeepEqual(actions, [{ 
     type: 'FETCH_USER_FULFILLED', 
     payload: mockResponse 
    }]); 
    }); 
+1

Je vois la solution est correcte. Je reçois "TypeError: Impossible de lire la propriété '_location' de null", bien qu'il soit en dehors du problème. – Oscar

+0

Je me cognais la tête dessus pendant quelques heures ... j'ai essayé ceci et j'ai eu l'erreur "... toArray n'est pas une fonction". –

+0

ok, l'ajout de l'import 'rxjs/add/operator/toArray' l'a résolu ... –