Testing React Native Apps
En Facebook, usamos Jest para testear aplicaciones de React Native.
Get a deeper insight into testing a working React Native app example by reading the following series: Part 1: Jest – Snapshot come into play and Part 2: Jest – Redux Snapshots for your Actions and Reducers.
Setup #
A partir de la versión 0.38 de react-native, Jest viene con una configuración incluida por defecto cuando se ejecuta: react-native init
. La siguiente configuración debería ser automáticamente añadida a tu archivo package.json:
// package.json "scripts": { "test": "jest" }, "jest": { "preset": "react-native" }
Nota: si estás actualizando tu applicación de react-native y previamente usaste el "preset" jest-react-native
, elimina la dependecia de tu archivo package.json
y cambia el "preset" por react-native
en su lugar.
Simplemente ejecuta npm test
para ejecutar los tests con Jest.
Test de Instantánea #
Creemos un test de instantánea para un componente pequeño con unas cuantas vistas y componentes de texto con algunos 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> ); } }
Ahora vamos usar las características render y snapshot de React y Jest para interactuar con los componentes y capturar lo que renderea, creando un archivo snapshot:
// __tests__/Intro-test.js import 'react-native'; import React from 'react'; import Intro from '../Intro'; // Note: test renderer must be required after react-native. import renderer from 'react-test-renderer'; test('renders correctly', () => { const tree = renderer.create( <Intro /> ).toJSON(); expect(tree).toMatchSnapshot(); });
Cuando ejecutas npm test
o jest
, se producirá un archivo de salida como éste:
// __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> `;
La próxima vez que ejecutes la prueba, lo que se renderee será comparado con el snapshot previamente creado. El snapshot deberá ser entregado a lo largo de los cambios en código. Cuando una prueba con snapshot falla, necesitará inspeccionar si se da por un cambio intencional o no intencional. Si el cambio es intencional, puede invocar Jest con jest -u
para sobre escribir en snapshot existente.
El código para este ejemplo está disponible en examples/react-native.
Configuración preestablecida #
El "preset" configura el entorno con una idea muy clara y basado en lo que ha resultado útil en Facebook. Todas las opciones de configuración puede ser sobrescritas así como personalizarse cuando no se usa un "preset".
Entorno #
react-native
viene con un "preset" para Jest, así que el campo jest.preset
de tu package.json
debería apuntar a react-native
. El "preset" es un entorno de node que refleja el entorno de una applicación de React Native. Ya que no carga el DOM o APIs del navegador, mejora de gran manera el tiempo de inicio de Jest.
Personalización de transformIgnorePatterns #
La opción transformIgnorePatterns
ser puede usar para deteminar si ciertos archivos deben ser transformados con "babel". Muchos módulos "npm" de react-native por desgracia no precompilan su código fuente antes de publicar.
Por defecto, el "preset" jest-react-native solo procesar los propios archivos fuente del proyecto y de react-native. Si tienes dependencias de "npm" que deben ser transformadas, puedes customizar está opción de configuración agregando módulos a la lista que no sean de react-native:
"transformIgnorePatterns": [ "node_modules/(?!(react-native|my-project|react-native-button)/)" ]
setupFiles #
Si quieres proporcionar configuración adicional para cada archivo de test, la opción setupFiles
de la configuración se puede usar para especificar scripts de instalación.
moduleNameMapper #
La opción moduleNameMapper
se puede usar para mapear una ruta de acceso de un módulo a otro diferente. Por defecto, este "preset" mapea todas las imágenes a un "stub" módulo de la imagen, pero si un módulo no puede ser encontrado, está opción de la configuración puede ayudar:
"moduleNameMapper": { "my-module.js": "<rootDir>/path/to/my-module.js" }
Consejos #
Simulacros de módulos nativos usando jest.mock #
El preset de "Jest" que viene con react-native
incluye unos cuantos "mocks" por defecto que se aplican en un repositorio de react-native. Sin embargo, algunos componentes de react-native o de terceros dependen de código nativo para renderizarse. En tales casos, el sistema de "mocking" manual de Jest, puede ayudar a evitar la implementación subyacente.
Por ejemplo, si tu código depende de una component de video nativo de terceros llamado react-native-video
, podrías querer simularlo con un "mock" manual tal que así:
jest.mock('react-native-video', () => 'Video');
Esto renderizará el componente como < Video {...props} / >
con todas sus propiedades en el resultado saliente de la instantánea.
A veces es necesario proporcionar un "mock" manual más complejo. Por ejemplo, si si quieres promover los "prop types" o "static fields" de un componente native a un "mock", puedes devolver un componente diferente de React desde un "mock" a través de este script auxiliar de jest-react-native:
jest.mock('path/to/MyNativeComponent', () => { const mockComponent = require('react-native/jest/mockComponent'); return mockComponent('path/to/MyNativeComponent'); });
O si prefieres crear tu propio "mock" manual, puedes hacer algo así:
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; });
En otros casos, puede que quieras hacer un "mock" a un módulo nativo que no es un componente de React. Se puede aplicar la misma técnica. Recommendamos que se inspeccione el código fuente del módulo nativo y logear el módulo cuando se ejecute una app de react native en un dispositivo real, para entonces modelar un "mock" manual basado en el módulo real.
Si acabas haciendo un "mock" de los mismos módulos una y otra vez, se recomienda definir estos "mocks" en un archivo separado y añadirlo a la lista de setupFiles
.
requiere react-native antes que el renderer de tests #
Actualmente se requiere incluir react-native antes de cargar renderer de tests:
import 'react-native'; // Require after react-native import renderer from 'react-test-renderer';
@providesModule
#
Si quieres usar el sistema de módulos de Facebook @providesModule
a través de paquetes "npm", la opción de configuración por defecto, "haste", debe ser sobrescrita y módulos de "npm" deben añadirse a providesModuleNodeModules
:
"haste": { "defaultPlatform": "ios", "platforms": ["android", "ios"], "providesModuleNodeModules": [ "react", "react-native", "my-awesome-module", "my-text-component" ] },
Si quieres probar una plataforma predeterminada diferente o si estás construyendo para otras plataformas, la opción de configuración defaultPlatform
y platforms
se puede actualizar.