Testes em React-Redux

Testando Redux

Como fazer?

Para testarmos aplicações em Redux utilizamos uma lógica similar a testes com o React-Router, ou seja, trocamos a função render() padrão do RTL, pelo render da biblioteca, no caso do Redux, renderWithRedux().

Assim como no React-Router, nosso render() para testes da biblioteca não vem pronto, devemos criá-lo como uma função. Fazendo isso, basta chamar essa função antes dos testes e pronto, podemos trabalhar o RTL normalmente.

Sintaxe

A função renderWithRedux() recebe dois parâmetros, o primeiro é o componente que queremos renderizar e o segundo é uma desconstrução de objeto representando um Store. Dessa forma podemos criar um Store apenas para o ambiente de testes e manipulá-lo livremente.

A desconstrução de objeto no segundo parâmetro deve conter o initialState, o Store, que por sua vez recebe a função createStore(), essa que recebe por parâmetro o reducer e o initialState.

O retorno da função renderWithRedux() deve ser um objeto de duas chaves, a primeira chave representa a desconstrução de um componente renderizado através da função render() padrão, sendo que esse componente deve estar “encapsulado” pelo componente Provider.

Já a segunda chave é a própria store criada através da desconstrução de objeto nos parâmetros da função.

const renderWithRedux = (
  component,
  { initialState, store = createStore(reducer, initialState) } = {}
) => {
  return {
    ...render(
      <Provider store={store}>
        {component}
      </Provider>
    ),
    store,
  };
};

Por ser uma sintaxe complexa, podemos simplesmente “copiar e colar” essa função sempre que necessário, assim depois que o componente for renderizado, basta seguir o passo a passo de um teste comum do RTL.

A única ressalva fica em relação a função createStore(), chamada no parâmetro da função renderWithRedux(), caso utilizemos um combineReducer em nossa Store original da aplicação, devemos utilizá-lo no parâmetro também.

Além de que os Reducers que o combineReducer irá receber, devem ter os mesmos nomes dos originais, afinal estamos fazendo uma desconstrução de objeto, logo o nome das chaves tem que ser respeitados.

const renderWithRedux = (
  component,
  {
    initialState,
    store = createStore(combineReducers({ myReducer }), initialState),
  } = {}
) => {
  return {
    ...render(
      <Provider store={store}>
        {component}
      </Provider>
    ),
    store,
  };
};

Testes Assíncronos no Redux

Como fazer?

Assim como nos testes síncronos, ainda precisamos utilizar a função renderWithRedux(), sendo que a maior diferença se dá na criação da Store personalizada para os testes, onde além de precisarmos passar o estado inicial e o Reducer, também é necessário passar o Redux-Thunk através do applyMiddleware().

Sintaxe

A sintaxe geral continua a mesma como dito anteriormente, com a pequena diferença que precisamos utilizar o applyMiddleware(). Também é interessante frisar que podemos dividir a função em mais pedaços, ou não.

Uma função

const renderWithRedux = (
  component,
  {
    initialState,
    store = createStore(
      combineReducers({ reducer }),
      initialState,
      applyMiddleware(thunk)
    ),
  } = {}
) => ({
  ...render(
    <Provider store={store}>
      {component}
    </Provider>
  ),
  store,
});

Dividindo em duas funções

const createMockStore = (initialState) =>
  createStore(
    combineReducers({ reducer }),
    initialState,
    applyMiddleware(thunk)
  );

const renderWithRedux = (
  component,
  { initialState, store = createMockStore(initialState) } = {}
) => ({
  ...render(
    <Provider store={store}>
      {component}
    </Provider>
    ),
  store,
});

Testes com Redux + Router

Como fazer?

Para testarmos components que estão dentro de uma rota e conectados a Store, precisamos de uma função (helper) mais completa. Esse helper deverá executar tanto o encapsulamento por Rota, quanto pelo Provider do Redux.

Sintaxe

A sintaxe desse helper é consideravelmente mais complexa que a dos outros, porém por se tratar de um helper podemos simplesmente "copiar e colar" conforme a necessidade.

O helper renderWithRuterAndRedux() consiste em uma função com dois parâmetros, o primeiro parâmetro é obrigatório e consiste no componente que desejamos renderizar.

Já o segundo parâmetros é opcional e é um objeto com quatro chaves, sendo que todas essas chaves também são opcionais, logo podemos passar todas as chaves ou apenas uma.

O objeto do segundo parâmetro possue as seguintes chaves:

  • initialState: refere-se ao estado inicial da Store, o que nós permite mocka-lá
  • store: como o nome indica, é a Store do Redux, sendo assim podemos passar uma Store personalizada
  • initialEntries: é um Array com caminhos do Router, sendo assim podemos criar uma "trilha" para o React-Router
  • history: refere-se ao histórico de paginação do Router.

Lembrando que todas as chaves do objeto do segundo parâmetro devem conter valores padrões, assim como o objeto em si.

const renderWithRouterAndRedux = (
  component,
  {
    initialState = {},
    store = createStore(rootReducers, initialState),
    initialEntries = ['/'],
    history = createMemoryHistory({ initialEntries }),
  } = {},
) => ({
  ...render(
    <Router history={ history }>
      <Provider store={store}>
        {component}
      </Provider>
    </Router>
  ),
  history,
  store,
});

59