2015-11-19 42 views
5

Qual è il modo migliore per provare questa funzionetest redux azioni che chiama un API

export function receivingItems() { 
    return (dispatch, getState) => { 
    axios.get('/api/items') 
     .then(function(response) { 
     dispatch(receivedItems(response.data)); 
     }); 
    }; 
} 

questo è attualmente quello che ho

describe('Items Action Creator',() => { 
    it('should create a receiving items function',() => { 
    expect(receivingItems()).to.be.a.function; 
    }); 
}); 

risposta

11

Da Redux “Writing Tests” ricetta:

Per i creatori di azione asincrone utilizzando Redux Thunk o altro middleware, è meglio prendere in giro tutto il negozio Redux per le prove. È comunque possibile utilizzare applyMiddleware() con un negozio fittizio, come illustrato di seguito (è possibile trovare il seguente codice in redux-mock-store). È inoltre possibile utilizzare nock per simulare le richieste HTTP.

function fetchTodosRequest() { 
    return { 
    type: FETCH_TODOS_REQUEST 
    } 
} 

function fetchTodosSuccess(body) { 
    return { 
    type: FETCH_TODOS_SUCCESS, 
    body 
    } 
} 

function fetchTodosFailure(ex) { 
    return { 
    type: FETCH_TODOS_FAILURE, 
    ex 
    } 
} 

export function fetchTodos() { 
    return dispatch => { 
    dispatch(fetchTodosRequest()) 
    return fetch('http://example.com/todos') 
     .then(res => res.json()) 
     .then(json => dispatch(fetchTodosSuccess(json.body))) 
     .catch(ex => dispatch(fetchTodosFailure(ex))) 
    } 
} 

può essere testata come:

import expect from 'expect' 
import { applyMiddleware } from 'redux' 
import thunk from 'redux-thunk' 
import * as actions from '../../actions/counter' 
import * as types from '../../constants/ActionTypes' 
import nock from 'nock' 

const middlewares = [ thunk ] 

/** 
* Creates a mock of Redux store with middleware. 
*/ 
function mockStore(getState, expectedActions, done) { 
    if (!Array.isArray(expectedActions)) { 
    throw new Error('expectedActions should be an array of expected actions.') 
    } 
    if (typeof done !== 'undefined' && typeof done !== 'function') { 
    throw new Error('done should either be undefined or function.') 
    } 

    function mockStoreWithoutMiddleware() { 
    return { 
     getState() { 
     return typeof getState === 'function' ? 
      getState() : 
      getState 
     }, 

     dispatch(action) { 
     const expectedAction = expectedActions.shift() 

     try { 
      expect(action).toEqual(expectedAction) 
      if (done && !expectedActions.length) { 
      done() 
      } 
      return action 
     } catch (e) { 
      done(e) 
     } 
     } 
    } 
    } 

    const mockStoreWithMiddleware = applyMiddleware(
    ...middlewares 
)(mockStoreWithoutMiddleware) 

    return mockStoreWithMiddleware() 
} 

describe('async actions',() => { 
    afterEach(() => { 
    nock.cleanAll() 
    }) 

    it('creates FETCH_TODOS_SUCCESS when fetching todos has been done', (done) => { 
    nock('http://example.com/') 
     .get('/todos') 
     .reply(200, { todos: ['do something'] }) 

    const expectedActions = [ 
     { type: types.FETCH_TODOS_REQUEST }, 
     { type: types.FETCH_TODOS_SUCCESS, body: { todos: ['do something'] } } 
    ] 
    const store = mockStore({ todos: [] }, expectedActions, done) 
    store.dispatch(actions.fetchTodos()) 
    }) 
}) 
+0

grazie Dan! molto chiaro! –

+2

Ricorda anche che 'createMockStore' è stato pubblicato come pacchetto: https://github.com/arnaudbenard/redux-mock-store –

+0

Puoi testare le azioni asincrone in modo sincrono utilizzando https://github.com/wix/redux -testkit – yedidyak

2

vorrei usare uno stub axios (ad esempio utilizzando mock-require) e scrivere un test che in realtà chiama receivingItems()(dispatch, getState) e si assicura che dispatch venga chiamato con i dati corretti.

1

ho risolto questo in un modo diverso: iniettare Axios come una dipendenza di azione. Preferisco questo approccio per le dipendenze 'rewiring'.

Quindi ho utilizzato lo stesso approccio per testare componenti collegati a Redux. Quando esporto azioni esporto due versioni: una con (da utilizzare per i componenti) e una senza (per il test) le dipendenze di bind.

Ecco come il mio file actions.js assomiglia:

import axios from 'axios' 

export const loadDataRequest =() => { 
    return { 
    type: 'LOAD_DATA_REQUEST' 
    } 
} 
export const loadDataError =() => { 
    return { 
    type: 'LOAD_DATA_ERROR' 
    } 
} 
export const loadDataSuccess = (data) =>{ 
    return { 
    type: 'LOAD_DATA_SUCCESS', 
    data 
    } 
} 
export const loadData = (axios) => { 
    return dispatch => { 
    dispatch(loadDataRequest()) 
    axios 
     .get('http://httpbin.org/ip') 
     .then(({data})=> dispatch(loadDataSuccess(data))) 
     .catch(()=> dispatch(loadDataError())) 
    } 
} 
export default { 
    loadData: loadData.bind(null, axios) 
} 

Poi il test con jest (actions.test.js):

import { loadData } from './actions' 

describe('testing loadData',()=>{ 
    test('loadData with success', (done)=>{ 

    const get = jest.fn() 
    const data = { 
     mydata: { test: 1 } 
    } 
    get.mockReturnValue(Promise.resolve({data})) 

    let callNumber = 0 
    const dispatch = jest.fn(params =>{ 
     if (callNumber===0){ 
     expect(params).toEqual({ type: 'LOAD_DATA_REQUEST' }) 
     } 
     if (callNumber===1){ 
     expect(params).toEqual({ 
      type: 'LOAD_DATA_SUCCESS', 
      data: data 
     }) 
     done() 
     } 
     callNumber++ 
    }) 
    const axiosMock = { 
     get 
    } 
    loadData(axiosMock)(dispatch) 
    }) 
}) 

Quando si usano le azioni all'interno di un componente di importare tutto:

import Actions from './actions'

E per spedizione:

Actions.loadData() // this is the version with axios binded.

Problemi correlati