2016-02-26 28 views
22

Sto provando a testare tutti gli scenari che la mia saga potrebbe seguire, ma non sono in grado di far accadere i comportamenti che voglio. Questo è piuttosto semplice, ho una richiesta HTTP (login), e voglio testare il successo e il fallimento dei casi prendendo in giro il mio metodo API.Come testare gli errori delle richieste API con Redux Saga?

Ma, sembra che il call effect non attivi la mia funzione api, non riesco ancora a capire come funziona, ma suppongo che il middleware abbia il compito di invocare la funzione, e dal momento che non lo faccio andare però il negozio sul mio test, non riesco a ottenere il risultato.

Quindi la mia domanda è: come puoi testare la tua saga quando hai bisogno di inviare diverse azioni (in genere successo o fallimento) accanto alla tua chiamata asincrona?

ho cercato un esempio, ho trovato saghe con successo e fallire ma il caso fallire è mai testato, ad esempio, nell'esempio carrello here

SAGA.JS

export function* login(action) { 
    try { 
    const user = yield call(api.login, action); 
    return yield put(actions.loginSuccess(user)); 
    } catch(e) { 
    yield put(actions.loginFail(e)); 
    } 
} 

export default function* rootAuthenticationSagas() { 
    yield* takeLatest(LOGIN, login); 
} 

test.js

describe('login',() => { 
    context('When it fails',() => { 
    before('Stub the api',() => { 
     sinon.stub(api, 'login',() => { 
     // IT NEVER COMES HERE ! 
     return Promise.reject({ error: 'user not found' }); 
     }); 
    }); 

    it('should return a LOGIN_FAIL action',() => { 
     const action = { 
     payload: { 
      name: 'toto', 
      password: '123456' 
     } 
     }; 
     const generator = login(action); 

     // THE CALL YIELD 
     generator.next(); 

     const expectedResult = put({ type: 'LOGIN_FAIL', payload: { error: 'user not found' } }); 
     expect(generator.next().value).to.be.eql(expectedResult); // FAIL BECAUSE I GET A LOGIN_SUCCESS INSTEAD OF A FAIL ONE 
    }); 
    }); 
}); 

risposta

36

Mark’s answer è corretto. Il middleware esegue queste istruzioni. Ma questo ti semplifica la vita: nel test, puoi fornire come vuoi come argomento a next(), e la funzione generatore lo riceverà come risultato di yield. Questo è esattamente ciò che fa il middleware di saga (eccetto per il fatto che in realtà attiva una richiesta invece di darti una risposta falsa).

Per rendere yield ottenere un valore arbitrario, passarlo a next(). Per farlo "ricevere" un errore, passalo a throw(). Nel tuo esempio:

it('should return a LOGIN_FAIL action',() => { 
    const action = { 
    payload: { 
     name: 'toto', 
     password: '123456' 
    } 
    }; 
    const generator = login(action); 

    // Check that Saga asks to call the API 
    expect(
    generator.next().value 
).to.be.eql(
    call(api.login, action) 
); 

    // Note that *no actual request was made*! 
    // We are just checking that the sequence of effects matches our expectations. 

    // Check that Saga reacts correctly to the failure 
    expect(
    generator.throw({ 
     error: 'user not found' 
    }).value 
).to.be.eql(
    put({ 
     type: 'LOGIN_FAIL', 
     payload: { error: 'user not found' } 
    }) 
); 
}); 
7

corretto - se ho capito bene, il punto centrale di Redux-saga è che la funzione saga utilizza le API saga di restituire gli oggetti che descrivono ° L'azione, e poi il middleware in seguito guarda quegli oggetti per eseguire effettivamente il comportamento. Quindi, un'istruzione yield call(myApiFunction, "/someEndpoint", arg1, arg2) in una saga potrebbe restituire un oggetto che assomigliasse a {effectType : CALL, function: myApiFunction, params: [arg1, arg2]}.

È possibile ispezionare l'origine di redux-saga per vedere esattamente quali sono effettivamente gli oggetti dichiarativi e creare un oggetto di confronto da confrontare nel test, oppure utilizzare le stesse funzioni API per creare gli oggetti (che credo sia cosa fa redux-saga nel loro codice di test).

0

È inoltre potrebbe desiderare di utilizzare una libreria helper per testare le vostre saghe, come redux-saga-testing.

Disclaimer: Ho scritto questa libreria per risolvere il problema esattamente lo stesso

Questa libreria renderà il vostro look test come qualsiasi altro test (sincrono), che è molto più facile ragionare su che chiamare generator.next() manualmente.

Prendendo il tuo esempio, si potrebbe scrivere i test come segue:

(che sta utilizzando la sintassi Jest, ma è sostanzialmente lo stesso con la moka, è completamente prova biblioteca-agnostico)

import sagaHelper from 'redux-saga-testing'; 
import { call, put } from 'redux-saga/effects'; 
import actions from './my-actions'; 
import api from './your-api'; 

// Your example 
export function* login(action) { 
    try { 
     const user = yield call(api.login, action); 
     return yield put(actions.loginSuccess(user)); 
    } catch(e) { 
     yield put(actions.loginFail(e.message)); // Just changed that from "e" to "e.message" 
    } 
} 


describe('When testing a Saga that throws an error',() => { 
    const it = sagaHelper(login({ type: 'LOGIN', payload: 'Ludo'})); 

    it('should have called the API first, which will throw an exception', result => { 
     expect(result).toEqual(call(api, { type: 'LOGIN', payload: 'Ludo'})); 
     return new Error('Something went wrong'); 
    }); 

    it('and then trigger an error action with the error message', result => { 
     expect(result).toEqual(put(actions.loginFail('Something went wrong'))); 
    }); 
}); 

describe('When testing a Saga and it works fine',() => { 
    const it = sagaHelper(login({ type: 'LOGIN', payload: 'Ludo'})); 

    it('should have called the API first, which will return some data', result => { 
     expect(result).toEqual(call(api, { type: 'LOGIN', payload: 'Ludo'})); 
     return { username: 'Ludo', email: '[email protected]' }; 
    }); 

    it('and then call the success action with the data returned by the API', result => { 
     expect(result).toEqual(put(actions.loginSuccess({ username: 'Ludo', email: '[email protected]' }))); 
    }); 
}); 

Altri esempi (usando Jest, Mocha e AVA) su GitHub.

Problemi correlati