Testing React Native Apps
No Facebook, nós usamos Jest para testar aplicações React Native.
Obtenha uma visão mais profunda em como testar um app React Native de exemplo lendo a seguinte série: Parte 1: Jest – Snapshot entra em jogo (em inglês) e Parte 2: Jest – Snapshots Redux para suas Actions e Reducers (em inglês).
Instalação #
A partir da versão 0.38 de react-native, uma instalação Jest é incluída por padrão, ao executar react-native init
. A seguinte configuração deve ser automaticamente adicionada ao seu arquivo package.json:
// package.json "scripts": { "test": "jest" }, "jest": { "preset": "react-native" }
Nota: Se você estiver atualizando seu aplicativo react-native e anteriormente usou a predefinição jest-react-native
, remova a dependência de seu arquivo package.json
e altere a predefinição para react-native
em vez disso.
Basta executar npm test
para executar testes com Jest.
Teste Snapshot #
Vamos criar um teste snapshot para um pequeno componente "intro" com alguns "views" e componentes de texto e alguns estilos:
// Intro.js import React, {Component} from 'react'; import { StyleSheet, Text, View, } from 'react-native'; const styles = StyleSheet.create({ container: { alignItems: 'center', backgroundColor: '#F5FCFF', flex: 1, justifyContent: 'center', }, instructions: { color: '#333333', marginBottom: 5, textAlign: 'center', }, welcome: { fontSize: 20, margin: 10, textAlign: 'center', }, }); export default class Intro extends Component { render() { return ( <View style={styles.container}> <Text style={styles.welcome}> Welcome to React Native! </Text> <Text style={styles.instructions}> This is a React Native snapshot test. </Text> </View> ); } }
Agora vamos usar o renderer de teste do React e o recurso de snapshot do Jest para interagir com o componente e capturar a saída renderizada e criar um arquivo de snapshot:
// __tests__/Intro-test.js import 'react-native'; import React from 'react'; import Intro from '../Intro'; // Nota: o renderer de teste deve ser requerido após react-native. test('renderiza corretamente', () => { const tree = renderer.create( <Intro /> ).toJSON(); expect(tree).toMatchSnapshot(); });
Quando você executa o npm test
ou jest
, isto irá produzir um arquivo de saída como este:
// __tests__/__snapshots__/Intro-test.js.snap exports[`Intro renders correctly 1`] = ` <View style={ Object { "alignItems": "center", "backgroundColor": "#F5FCFF", "flex": 1, "justifyContent": "center", } }> <Text style={ Object { "fontSize": 20, "margin": 10, "textAlign": "center", } }> Welcome to React Native! </Text> <Text style={ Object { "color": "#333333", "marginBottom": 5, "textAlign": "center", } }> This is a React Native snapshot test. </Text> </View> `;
Da próxima vez que você executar os testes, a saída renderizada será comparada ao snapshot criado anteriormente. O snapshot deve ser comitado (committed, em inglês) junto com as alterações de código. Quando um teste de snapshot falhar, você precisa inspecionar se é uma mudança pretendida ou não intencional. Se a mudança é esperada, você pode invocar Jest com jest -u
para substituir o snapshot existente.
O código deste exemplo está disponível em examples/react-native.
Configuração predefinida #
A predefinição configura o ambiente e é muito opinativa e baseada no que tem se mostrado ser útil no Facebook. Todas as opções de configuração podem ser substituídas assim como eles podem ser personalizados quando nenhuma predefinição é usada.
Ambiente #
react-native
vem com uma pré-definição Jest, então o campo jest.preset
de seu package.json
deve apontar para react-native
. A predefinição é um ambiente node que imita o ambiente de um app React Native. Porque ele não carrega nenhum DOM ou APIs de navegador, melhora consideravelmente o tempo de inicialização do Jest.
personalização de transformIgnorePatterns #
A opção transformIgnorePatterns
pode ser usada para arquivos de lista branca ou lista negra de serem transformados com Babel. Muitos módulos npm de react-native infelizmente não pre-compilam seu código fonte antes de publicar.
Por padrão a predefinição jest-react-native só processa os arquivos fonte do projeto e react-native. Se você tem dependências npm que tem que ser transformadas, você pode personalizar esta opção de configuração por colocar módulos que não sejam o react-native em uma lista branca:
"transformIgnorePatterns": [ "node_modules/(?!(react-native|my-project|react-native-button)/)" ]
setupFiles #
Se você gostaria de fornecer configuração adicional para cada arquivo de teste, a opção de configuração setupFiles
pode ser usada para especificar os scripts de instalação.
moduleNameMapper #
O moduleNameMapper
pode ser usado para mapear um caminho de módulo para um módulo diferente. Por padrão a predefinição mapeia todas as imagens para um módulo de esboço de imagem, mas se um módulo não pode ser encontrado esta opção de configuração pode ajudar:
"moduleNameMapper": { "my-module.js": "<rootDir>/path/to/my-module.js" }
Dicas #
Simule (mock, em inglês) módulos nativos usando jest.mock #
A predefinição Jest interna do react-native
vem com algumas simulações (mocks, em inglês) padrão que são aplicadas em um repositório react-native. No entanto, alguns componentes react-native ou componentes de terceiros dependem de código nativo para serem renderizados. Em tais casos, o sistema manual de simulação do Jest pode ajudar a simular a implementação subjacente.
Por exemplo, se seu código depende de um componente de vídeo nativo de terceiros chamado react-native-video
convém esboçar ele com um simulação manual como esta:
jest.mock('react-native-video', () => 'Video');
Isto irá renderizar o componente como <Video {...props} />
com todas as suas propriedades no snapshot de saída.
Às vezes você precisa fornecer uma simulação manual mais complexa. Por exemplo, se você gostaria de transmitir os tipos das propriedades ou campos estáticos de um componente nativo para uma simulação (mock, em inglês), você pode retornar um componente React diferente a partir de uma simulação através deste auxiliar do jest-react-native:
jest.mock('path/to/MyNativeComponent', () => { const mockComponent = require('react-native/jest/mockComponent'); return mockComponent('path/to/MyNativeComponent'); });
Ou se você gostaria de criar sua própria simulação manual, você pode fazer algo parecido com isto:
jest.mock('Text', () => { const RealComponent = require.requireActual('Text'); const React = require('React'); class Text extends React.Component { render() { return React.createElement('Text', this.props, this.props.children); } } Text.propTypes = RealComponent.propTypes; return Text; });
Em outros casos, você pode querer simular um módulo nativo que não seja um componente React. A mesma técnica pode ser aplicada. Nós recomendamos inspecionar o código-fonte do módulo nativo e registrar o módulo ao executar um aplicativo react native em um dispositivo real e então modelar uma simulação manual após o módulo real.
Se você acabar por simular os mesmos módulos repetidamente vez após vez é recomendável definir estas simulações em um arquivo separado e adicioná-lo à lista de setupFiles
.
requerir react-native antes do renderer de testes #
Atualmente, é necessário requerir react-native antes de carregar o renderizador de teste:
import 'react-native'; // Requerir após react-native import renderer from 'react-test-renderer';
@providesModule
#
Se você gostaria de usar o sistema de módulo @providesModule
do Facebook através de um pacote npm, a opção de configuração padrão "haste" deve ser substituída e módulos npm devem ser adicionados ao providesModuleNodeModules
:
"haste": { "defaultPlatform": "ios", "platforms": ["android", "ios"], "providesModuleNodeModules": [ "react", "react-native", "meu-modulo-incrivel", "meu-componente-texto" ] },
Se você gostaria de testar uma plataforma padrão diferente ou se você está construindo para outras plataformas, a opção de configuração defaultPlatform
e platforms
pode ser atualizada.