59
Testes em React-Redux
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.
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,
};
};
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()
.
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.
const renderWithRedux = (
component,
{
initialState,
store = createStore(
combineReducers({ reducer }),
initialState,
applyMiddleware(thunk)
),
} = {}
) => ({
...render(
<Provider store={store}>
{component}
</Provider>
),
store,
});
const createMockStore = (initialState) =>
createStore(
combineReducers({ reducer }),
initialState,
applyMiddleware(thunk)
);
const renderWithRedux = (
component,
{ initialState, store = createMockStore(initialState) } = {}
) => ({
...render(
<Provider store={store}>
{component}
</Provider>
),
store,
});
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.
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