React Native & Jest: Hacer un mock del dispositivo y del tema actual
Cuando tienes una aplicación React Native y quieres testear algunos componentes. Probablemente tarde o temprano tendrás la necesidad de renderizar esos componentes desde un dispositivo iOS, iPad o Android.
La parte difícil no es agregar soporte para el dispositivo, eso viene por defecto en el framework. La parte complicada es decirle al framework de testing como quieres renderizar a ese componente.
A continuación voy a listar las soluciones que encontré en el proceso de agregar esos tests.
🔨 ¿Qué librerías estoy usando?
Para el siguiente tutorial, estoy utilizando:
- React: 18
- React Native: 0.70.2
- Jest: 29.1
- Typescript: 4.8
-
@testing-library/react-native
: 11.2.0 (para hacer testing de componentes JSX)
💻 Haciendo un mock del dispositivo
Detectando el sistema operativo con una función Helper
Como sabrás, debemos utilizar Platform.OS
para detectar desde que plataforma estamos ejecutando nuestro código. Para hacer más fácil de probar y / o reutilizar el código en diversos lugares de nuestro proyecto, prefiero tener un archivo “Helper”. En el siguiente ejemplo voy a utilizar la sintaxis de ESM pero, por supuesto, puedes utilizar la de CommonJS.
Agreguemos un método estático para evitar inicializar la clase. En lugar de new Helpers().isIpad()
, podemos utilizar Helpers.isIpad()
simplemente.
Escribiendo los tests
Vamos a probar que nuestra función puede correr sobre Android
Como puedes ver, utilizamos Platform.OS
para sobrescribir nuestra plataforma por cada test.
Por cierto, un dispositivo iPad será tratado como un dispositivo ios
. Entonces, ¿cómo podemos forzar a React Native para siempre retornar true
en la función Platform.isIpad()
?
Acá es donde los spies de Jest vienen en nuestra ayuda. Ya que isIpad
es una función getter, con los spies, hay una manera fácil de sobrescribirla. Utilizo mockReturnValueOnce
para especificar el retorno de la función la primera vez que es ejecutada.
En español,
mockReturnValueOnce
, se traduciría a algo comohacerMockRetornarUnaSolaVez
Para Typescript utilizaremos PlatformIOSStatic
, esta es una manera de decirle a TS que los tipos de Platform
se refieren a un dispositivo iOS y iPad.
Una vez aprendido eso. Podemos comenzar a agregar tests para cubrir cualquier caso raro.
Contaminación de tests
Ya que algunos tests pueden ejecutarse en paralelo. Podríamos tener una polución de tests. Significa que algunos valores de un test pueden influir en otros del mismo grupo. ¿Puedes ver cómo cada uno tiene un Platform.OS = 'ios'
?. Si quieres poner una plataforma por defecto, puedes encerrar tus tests dentro de una función describe
y después agregar un afterEach
. Con esto, podemos sobrescribirla y devolverla al estado inicial después de cada test.
🎨 Hacer un mock del hook useColorScheme
Listo, ahora podemos sobrescribir la plataforma. Pero ¿qué hay del modo oscuro?. Ya sea que estés usando Android o iOS, el modo oscuro será útil especialmente durante las noches o en entornos oscuros. Desde React Native, podemos codificar unas condiciones para estilos basados en él. Y tenemos acceso a useColorScheme
. ¿Cómo podemos hacerle un mock?
Primero, veamos un ejemplo de cómo se utiliza el hook para implementar el modo oscuro en un componente.
Mi componente ColoredStatusBar (Barra de Estado Coloreada)
Esta función cambia el color de la barra de estado validando si el Sistema Operativo es ios
y si está en modo claro. El archivo GlobalStyles
contiene declaraciones de los estilos de React Native como objetos. El hook useIsFocused
viene desde React Navigation para detectar si la aplicación se encuentra “enfocada” (activa).
Implementación actual del hook
Si vamos al código fuente de useColorScheme
nos encontraremos lo siguiente. Este es código de React Native de la versión mencionada antes.
Están utilizando un export
del tipo ESM para exportar una función por defecto. Hacerle un mock será un poco diferente.
Veamos como podemos hacer un test snapshot al componente.
Creando un test si el dispositivo es iPad y en modo oscuro
Algunas notas aquí
- Usamos acá
doMock
para evitar la polución de tests. Lee la documentación aquí . Y retornamosdark
a la primera llamada, porque si no lo hacemos, regresarálight
por defecto. - Estamos comprobando también que el hook
useIsFocused
es llamado al menos una vez (primer render) - Usamos
toJSON
para hacer un test snapshot del componente.