Testear Aplicaciones React Native
At Facebook, we use Jest to test React Native applications.
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
Starting from react-native version 0.38, a Jest setup is included by default when running react-native init. La siguiente configuración debería ser automáticamente añadida a tu archivo package.json:
{
"scripts": {
"test": "jest"
},
"jest": {
"preset": "react-native"
}
}
Run yarn test to run tests with Jest.
If you are upgrading your react-native application and previously used the jest-react-native preset, remove the dependency from your package.json file and change the preset to react-native instead.
Test de Instantánea
Let's create a snapshot test for a small intro component with a few views and text components and some styles:
import React, {Component} from 'react';
import {StyleSheet, Text, View} from 'react-native';
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>
);
}
}
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 Intro;
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:
import React from 'react';
import renderer from 'react-test-renderer';
import Intro from '../Intro';
test('renders correctly', () => {
const tree = renderer.create(<Intro />).toJSON();
expect(tree).toMatchSnapshot();
});
When you run yarn test or jest, this will produce an output file like this:
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. Se debería aplicar el snapshot junto con los cambios de código. Cuando una prueba con snapshot falla, necesitará inspeccionar si se da por un cambio intencional o no intencional. If the change is expected you can invoke Jest with jest -u to overwrite the existing snapshot.
The code for this example is available at 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 ships with a Jest preset, so the jest.preset field of your package.json should point to 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
The transformIgnorePatterns option can be used to specify which files shall be transformed by Babel. Many react-native npm modules unfortunately don't pre-compile their source code before publishing.
By default the jest-react-native preset only processes the project's own source files and react-native. If you have npm dependencies that have to be transformed you can customize this configuration option by including modules other than react-native by grouping them and separating them with the | operator:
{
"transformIgnorePatterns": ["node_modules/(?!((@)?react-native|my-project)/)"]
}
You can test which paths would match (and thus be excluded from transformation) with a tool like this.
transformIgnorePatterns will exclude a file from transformation if the path matches against any pattern provided. Splitting into multiple patterns could therefore have unintended results if you are not careful. In the example below, the exclusion (also known as a negative lookahead assertion) for foo and bar cancel each other out:
{
"transformIgnorePatterns": ["node_modules/(?!foo/)", "node_modules/(?!bar/)"] // not what you want
}
setupFiles
If you'd like to provide additional configuration for every test file, the setupFiles configuration option can be used to specify setup scripts.
moduleNameMapper
The moduleNameMapper can be used to map a module path to a different module. 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
The Jest preset built into react-native comes with a few default mocks that are applied on a react-native repository. However, some react-native components or third party components rely on native code to be rendered. En tales casos, el sistema de "mocking" manual de Jest, puede ayudar a evitar la implementación subyacente.
For example, if your code depends on a third party native video component called react-native-video you might want to stub it out with a manual mock like this:
jest.mock('react-native-video', () => 'Video');
This will render the component as <Video {...props} /> with all of its props in the snapshot output. See also caveats around Enzyme and React 16.
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 = jest.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.
If you end up mocking the same modules over and over it is recommended to define these mocks in a separate file and add it to the list of setupFiles.