Búsqueda de sitios web

Probando acciones asincrónicas de Redux con Typescript


Hola chicos, estoy tratando de probar mis creadores de acciones asíncronas con Typescript, pero estoy obteniendo un error de tipo que no puedo resolver.

Estas son mis acciones:

export const loadCurrentUserRequest = () => ({
  type: LOAD_CURRENT_USER_REQUEST
})

export const loadCurrentUserSuccess = (payload: any) => ({
  type: LOAD_CURRENT_USER_SUCCESS,
  payload
})

export const loadCurrentUserFailure = (payload: any) => ({
  type: LOAD_CURRENT_USER_FAILURE,
  payload
})

Y este es mi creador de acciones asíncronas:

export const loadCurrentUser = () => {
  return async (dispatch: Dispatch<any>) => {
    dispatch(loadCurrentUserRequest())
    try {
      const response = await get(`api/currentuser`)
      if (!response.ok) {
        dispatch(loadCurrentUserFailure({ type: null, message: 'error' }))
      } else {
        const json = await response.json()
        dispatch(loadCurrentUserSuccess(json))
      }
      return response
    } catch (err) {
      dispatch(loadCurrentUserFailure({ type: err.name, message: err.message }))
      logError(err.name, err.message)
      return err
    }
  }
}

La función 'get' es un middleware que he creado para manejar la llamada GET de 'fetch' (añade algunas cosas al encabezado, etc.).

Esto es mi prueba:

  it('create an action to load the current user', () => {
    const middlewares = [thunk]
    const mockStore = configureMockStore(middlewares)
    const store = mockStore()
    const expectActions = [{ type: LOAD_CURRENT_USER_REQUEST }]

    store.dispatch(actions.loadCurrentUser())
    expect(store.getActions()).toEqual(expectActions)
  })

Estoy recibiendo este error en mi consola:

Argument of type '(dispatch: Dispatch<any>) => Promise<any>' is not assignable to parameter of type 'AnyAction'.
  Property 'type' is missing in type '(dispatch: Dispatch<any>) => Promise<any>' but required in type 'AnyAction'.

No estoy seguro de lo que he hecho mal aquí, miré la forma de ejemplo de redux para probar creadores de acciones asincrónicas y esto es similar. No puedo averiguar de dónde proviene mi problema.

Sé que tendré que simular mis llamadas a la API fetch y actualizar mi variable expectActions, pero el código no está compilando por eso..

Respuesta #1

Aquí está la solución:

index.ts:

import fetch from 'node-fetch';
import { ThunkDispatch } from 'redux-thunk';
import { AnyAction } from 'redux';

const logError = (name, message) => console.error(`name: ${name}, message: ${message}`);
const get = url => fetch(url);

export const LOAD_CURRENT_USER_REQUEST = 'LOAD_CURRENT_USER_REQUEST';
export const LOAD_CURRENT_USER_SUCCESS = 'LOAD_CURRENT_USER_SUCCESS';
export const LOAD_CURRENT_USER_FAILURE = 'LOAD_CURRENT_USER_FAILURE';

export const loadCurrentUserRequest = () => ({
  type: LOAD_CURRENT_USER_REQUEST
});

export const loadCurrentUserSuccess = (payload: any) => ({
  type: LOAD_CURRENT_USER_SUCCESS,
  payload
});

export const loadCurrentUserFailure = (payload: any) => ({
  type: LOAD_CURRENT_USER_FAILURE,
  payload
});

export const loadCurrentUser = () => {
  return async (dispatch: ThunkDispatch<any, any, AnyAction>) => {
    dispatch(loadCurrentUserRequest());
    try {
      const response = await get(`api/currentuser`);
      if (!response.ok) {
        dispatch(loadCurrentUserFailure({ type: null, message: 'error' }));
      } else {
        const json = await response.json();
        dispatch(loadCurrentUserSuccess(json));
      }
      return response;
    } catch (err) {
      dispatch(loadCurrentUserFailure({ type: err.name, message: err.message }));
      logError(err.name, err.message);
      return err;
    }
  };
};

Prueba unitaria:

import configureStore from 'redux-mock-store';
import thunk, { ThunkDispatch } from 'redux-thunk';
import * as actions from './';
import { AnyAction } from 'redux';
import fetch from 'node-fetch';

jest.mock('node-fetch', () => jest.fn());

const initialState = {};
type State = typeof initialState;
const middlewares = [thunk];
const mockStore = configureStore<State, ThunkDispatch<State, any, AnyAction>>(middlewares);
const store = mockStore(initialState);

describe('action creators', () => {
  describe('#loadCurrentUser', () => {
    afterEach(() => {
      store.clearActions();
    });
    it('load current user success', async () => {
      const userMocked = { userId: 1 };
      (fetch as jest.MockedFunction<any>).mockResolvedValueOnce({
        ok: true,
        json: jest.fn().mockResolvedValueOnce(userMocked)
      });
      await store.dispatch(actions.loadCurrentUser());
      expect(fetch).toBeCalledWith('api/currentuser');
      expect(store.getActions()).toEqual([
        { type: actions.LOAD_CURRENT_USER_REQUEST },
        { type: actions.LOAD_CURRENT_USER_SUCCESS, payload: userMocked }
      ]);
    });

    it('load current user failed', async () => {
      (fetch as jest.MockedFunction<any>).mockResolvedValueOnce({ ok: false });
      await store.dispatch(actions.loadCurrentUser());
      expect(fetch).toBeCalledWith('api/currentuser');
      expect(store.getActions()).toEqual([
        { type: actions.LOAD_CURRENT_USER_REQUEST },
        {
          type: actions.LOAD_CURRENT_USER_FAILURE,
          payload: {
            type: null,
            message: 'error'
          }
        }
      ]);
    });

    it('load current user failed when fetch error', async () => {
      (fetch as jest.MockedFunction<any>).mockRejectedValueOnce(new Error('fetch error'));
      await store.dispatch(actions.loadCurrentUser());
      expect(fetch).toBeCalledWith('api/currentuser');
      expect(store.getActions()).toEqual([
        { type: actions.LOAD_CURRENT_USER_REQUEST },
        {
          type: actions.LOAD_CURRENT_USER_FAILURE,
          payload: {
            type: 'Error',
            message: 'fetch error'
          }
        }
      ]);
    });
  });
});

Resultado de la prueba unitaria con 100% de cobertura:

 PASS  src/stackoverflow/54693637/index.spec.ts
  action creators
    #loadCurrentUser
      ✓ load current user success (8ms)
      ✓ load current user failed (1ms)
      ✓ load current user failed when fetch error (8ms)

  console.error src/stackoverflow/54693637/index.ts:3541
    name: Error, message: fetch error

----------|----------|----------|----------|----------|-------------------|
File      |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files |      100 |      100 |      100 |      100 |                   |
 index.ts |      100 |      100 |      100 |      100 |                   |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        3.32s, estimated 5s

Aquí está la demostración completada: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/54693637