Hola a todos, mi nombre es Yaroslav Astafiev, y hoy me gustaría realizar una visita guiada para probar ReactJS. No profundizaré en la complejidad de probar aplicaciones web usando ciertas bibliotecas (guiado por el enfoque "es difícil probar solo un código incorrecto"), a cambio intentaré diversificar sus horizontes. Entonces, en este artículo, React es más una ocasión para reunir enfoques de prueba, un punto de partida que combina hipsters y tecnología. Sería más correcto incluso decir que hablaremos sobre los principios de las pruebas en general con ilustraciones en ReactJS (y no solo).
Si se considera un gurú de las pruebas,
omita la primera mitad del artículo , se trata de los principios básicos de las pruebas. Si la segunda parte no revela nada nuevo para usted, venga a trabajar y enseñemos cómo hacerlo.

Si la introducción no causó un ataque de sinestesia, bienvenido a cat.
Pruebas unitarias
Unit Jest es una biblioteca para probar JavaScript. No me gusta esto, tome
otro , pero
Ava es mejor. Aquí todo es simple: haga clic en el botón mágico y asegúrese de que un cierto valor de "0" haya cambiado a "1":
import React from "react" import { MyButton } from "../src/components/dummy/myButton" import renderer from "react-test-renderer" test("MyButton has onPress fn", () => { let x = 0 const instance = renderer .create(<MyButton onPress={() => x++} />) .getInstance() expect(instance.handlePress).toBeDefined() expect(x).toBe(0) instance.props.handlePress() expect(x).toBe(1) })
Ahora tienes todas las habilidades necesarias para probar el botón mágico. Desafortunadamente, estas habilidades no están relacionadas con la vida real. El componente React no puede estar tan bien aislado, y el aislamiento es uno de los principios principales de las pruebas unitarias. De alguna manera, es necesario eliminar todos los componentes que de alguna manera participan en el método de renderizado, con la excepción del probado. Y hay una solución: a las personas inteligentes se les ocurrió mockAPI para esto.
La esencia de Mock es simple: todo lo que no es nuestro es
Mock / Stub / Fake / Dummy / Spy, etc. "Emulamos" la forma en que necesitamos el comportamiento real de un componente, que puede tener una lógica compleja, en datos de prueba preparados previamente y creemos que todos los componentes emulados funcionan perfectamente si se les ingresan los parámetros correctos.
Hay una biblioteca
jest-fetch-mock para
jest , en ella puedes definir moki globalmente. Si no le gusta esta opción, puede "mojar" cada componente que necesita en la prueba por separado.
Una función pura en la misma entrada siempre devuelve la misma respuesta. En consecuencia, si en nuestra lógica de negocios los componentes tienen funciones / componentes "no limpios", entonces en las pruebas unitarias también deberán "borrarse" (pero para las pruebas unitarias esta regla no siempre es cierta). Un ejemplo clásico es un componente de reacción que muestra la fecha y hora actuales en el formato que necesita, la fecha será diferente cada vez que realice las pruebas y no podrá escribir las pruebas unitarias correctas. Para todos aquellos que no están de acuerdo, puede complicar el ejemplo en el que su componente debe mostrar la fecha en formato relativo y resaltar las fechas que son más antiguas que un
año desde la fecha
actual en rojo.
En consecuencia, si tiene cosas dinámicas que dependen del tiempo / clima / presión, entonces simulacro redefinirá la llamada que necesita para que no haya dependencia de factores de terceros. Por lo tanto, no tiene que esperar
el 29 de febrero para tomar un examen caído.
Reglas de prueba de unidad
Los problemas descritos anteriormente y los métodos para resolverlos son intentos de un enfoque informal para las pruebas: cada prueba se ejecuta como quiere. En mi opinión, el cumplimiento de
tres reglas importantes de pruebas unitarias es suficiente:
- Determinismo
- Aislamiento
- Independencia de factores externos
- Sentido común
Regla uno: todas las pruebas deben ser deterministas. Si escribí una prueba en Windows, en una Mac también debería iniciarse y producir el mismo resultado. Los desarrolladores de Windows adoran olvidar que los nombres de archivo en los sistemas * nix distinguen entre mayúsculas y minúsculas. Y tiene suerte si las
pruebas caen dentro del marco de CI, y no la aplicación en producción .
La siguiente regla es el aislamiento. Mojamos todos los componentes no probados. Si esto es difícil de hacer, entonces es hora de refactorizar.
Por último, pero no menos importante: si hay datos que su aplicación recibe en tiempo de ejecución, también deben especificarse. Puede ser una configuración regional, tamaño de ventana, formato de fecha, formato de número de coma flotante, etc.
Pruebas de integración
Cuándo comenzar a escribir pruebas de integración es, en mi opinión, una pregunta abierta, y en cada equipo / producto individual, la decisión debe tomarse teniendo en cuenta los factores internos.
Puede adoptar un enfoque formal: lograr
una cobertura de
prueba unitaria del
80% (las pruebas mal escritas no deben revisarse / requerir que solo el código nuevo o modificado esté cubierto por las pruebas), luego realizar una auditoría completa y refactorización de todas las pruebas escritas con análisis de errores típicos, formalizar reglas internas para escribir pruebas y llevar a cabo tales redadas por año. Si después de todas las acciones descritas anteriormente, su cobertura de código de pruebas unitarias sigue siendo 80% +, entonces tiene un equipo maduro, o simplemente no es crítico con su código / pruebas. Si la cobertura del código se ha reducido, entonces necesita alcanzar una cobertura del 80% nuevamente y proceder a escribir pruebas de integración. Puede llegar de manera menos formal y simplemente guiarse por el sentido común: por ejemplo, para cada error que se haya jugado n veces, escriba una prueba o invente algo más, por ejemplo, arroje una moneda.
La segunda pregunta abierta:
¿qué pruebas se consideran integración ? Quizás dejarlo sin respuesta.

En las pruebas de integración, probamos el trabajo no de un componente, sino de varios componentes en un grupo. No hay reglas, pero el sentido común te dice:
- No pruebe cómo se representa, dónde se llama y cuándo terminará todo;
- No pruebe el trabajo de ReactJS, si no funciona, nada lo ayudará;
- No pruebe cómo funciona la máquina de estado React;
- prueba la lógica de negocios / modelo de datos / situaciones límite / algo que a menudo se rompe.
En tales pruebas, no debe entrar en detalles. Funcionan notablemente más tiempo y también es más difícil escribirlos, por lo que no debe dejarse llevar y cubrir todos los casos menores en la lógica de la aplicación. Esto es costoso en términos de alquiler de infraestructura y largo en términos de desarrollo y tiempo de ejecución de scripts. Alguien pasará sus vidas en esta rutina, y el
administrador de usuarios estará triste , y esperará nuevas funciones, y ...
Otra razón por la que no debe intentar probar todo es False Security (he tratado de escribir los puntos más importantes anteriores). Cada equipo debe leer sobre los errores del primer y segundo tipo, el lema de Neumann-Pearson y evaluar sus riesgos en términos de dinero, loros u otra medida de verdad aceptada en el equipo.
Pero hay excepciones a esta regla, así como a cualquier otra.
Olvida todo lo que se dice arriba :
- Al probar dependencias dinámicas. Cuando no sabe qué componente vendrá en tiempo de ejecución, lo renderiza, pero puede que no venga. También debe escribir una prueba para esto, nadie canceló el circuito bracker. O el componente incorrecto que esperaba o vendrá un componente roto. Por lo tanto, en este caso, escribimos una prueba de integración y renderizamos. Verificamos si todo funciona, si nada cae.
- Con pixel perfect (bueno, ya entiendes), el desarrollo tendrá que renderizar y diferenciar capturas de pantalla, y cada vez que la biblioteca de componentes se actualice a una nueva versión, actualiza las capturas de pantalla de referencia. Porque es más fácil
contratar a un nuevo diseñador para reconciliar que para arreglar.
Pruebas de instantáneas
La prueba de integración más simple es una instantánea:
- Tomamos un componente, lo renderizamos
- En el render escribimos console.log (esto)
- Copie los datos de referencia de la consola.
- Comparar
Si quieres confundirte un poco, te aconsejo que juegues con la biblioteca de
StoryBook . Esta es una biblioteca para pruebas de instantáneas, que incidentalmente envolvió la idea de
StyleGuidist : crear su propio sistema de diseño basado en componentes React.
La primera regla de la prueba de instantánea:
nunca digas, intenta probar los datos . Siempre deben ser estáticos, "encerrados" e independientes. La segunda regla: una
prueba de instantánea rota no significa que todo esté mal . Si es rojo, no es el hecho de que todo salió. Hay muchas opciones para hacer que el diseño sea el mismo, pero el árbol DOM era diferente. Por lo tanto, ignoramos los espacios en blanco, los atributos, las claves o no probamos lo que requiere tanto tiempo de soporte. O marcamos con nuestras manos lo que está roto, lo que no está. Arreglamos las pruebas rotas y reiniciamos el StoryBook en el modo de actualización simulada: el modo en que la prueba representará los componentes e insertará Instantánea como valor de referencia en la condición esperada.
xState y React Automata
ReactJS es algo complicado. Parece que la biblioteca es genial. Hice tres componentes, una clase: y la máquina de estado parece funcionar y el código es hermoso. Luego escribes en ReactJS durante seis meses, miras el código, una especie de tontería. No entiendes dónde están las muletas, dónde están las rutas, dónde está el estado, dónde están los escondites ... Luego piensas: bueno, haré lo que Facebook me aconseja: sujetaré los "hokeys", "ganchos", algo más y de repente te sorprenderé pensando cómo te lanzas hh. Ru en los intentos de encontrar un proyecto con desarrollo en la reacción desde cero, de modo que sin duda
hay que hacer todo muy bien ...
Todo es tan complicado que generalmente es imposible entender cómo funciona. Y funciona hasta que alguien se queja. Lo arreglamos, y lo arregla, pero se rompe ... Una de las salidas es una
máquina de estados, un conjunto de estados de aplicación deterministas y transiciones permitidas entre ellos. Y, como dicen en círculos estrechos, no escribió sobre la reacción si no cortó su máquina de estados.
Vale la pena recordar
xState . Esta es una máquina de estado determinista para JavaScript.
Puede crear una interfaz de usuario muy buena en
xState : puede encontrar un enlace al informe correspondiente en la documentación de la biblioteca
React Automata . A su vez, React Automata es la biblioteca en la que xState se adaptó en ReactJS. Además, es capaz de
generar pruebas para el estado de una máquina de estados .
Si tenemos el primer tic verdadero, la luz verde está encendida. Si el segundo es falso, se dibuja un perro gris y React Automata genera pruebas para las cuatro combinaciones de estos parámetros y valida el perro y los bulbos. Sin embargo, en algún momento querrás reducir la mitad de las pruebas, pero al principio estarás muy contento ... En cualquier caso, esta es una mirada conveniente a tus pruebas desde un lado, me recuerda mucho la idea de probar con el caos determinista.
Ciprés
Con la instantánea, más o menos lo descubrimos, puede ir hacia end2end. Tenemos todos los productos internos, por lo que las soluciones locales son nuestra única opción. Espero que tenga la oportunidad de usar soluciones en la nube, entonces
Cypress será útil.

Anteriormente, elegía probar marcos, tomaba bibliotecas para afirmación, bibliotecas para comparar XML complejos. Luego seleccionamos un controlador y un navegador para comenzar todo. Comenzaron y escribieron un montón de pruebas. Todo esto está consumiendo la infraestructura, necesita meter todo en la ventana acoplable, luego atornillar algo que mira las pruebas en dinámica, las analiza, muestra lo que está mal ...

Los chicos de Cypress lo hicieron todo por ti. Resolvieron varios problemas:
configurar un entorno de trabajo, escribir código, ejecutar y escribir pruebas. Si se rompe la prueba, puede mostrar una captura de pantalla resaltando lo que se ha roto. Es cierto que no funciona para teléfonos móviles, pero allí, por ejemplo, está
Detox . Esto, por supuesto, es difícil desde el punto de vista del umbral de entrada: tendrá que adaptar su aplicación a ella, reescribir un montón de archivos, etc. Pero si quieres, es posible.
Pruebas suaves
Existen tipos alternativos de pruebas que tampoco pueden llamarse buenas pruebas. Los llamo pruebas suaves. Por ejemplo, linter. Son utilizados principalmente por el front-end (e incluso, a veces, la inspiración se reduce a los javists). Hay muchas
linters :
ESLint ,
JSHint ,
Prettier ,
Standard ,
Clinton . Aconsejo a
Prettier: rápido, barato, alegre, fácil de configurar, funciona de forma inmediata .
Si desea confundirse, puede configurar ESLint. Permíteme darte un ejemplo clásico de un complemento para él: cuando un cliente encuentra comentarios con expresiones obscenas en tu código, generalmente jura. Los desarrolladores difíciles hacen comentarios en ruso para que el cliente no adivine. Pero el cliente adivinó ... usa un traductor de Google y descubrió todo lo que los desarrolladores piensan de él. La salida de la situación es desagradable, posiblemente con la pérdida de dinero o clientes. Para estos casos,
siempre puede desarrollar un complemento para ESLint , que encuentra las palabras "ruso nativo" en su código fuente y dice: "Oh, lo siento, rechace su confirmación".
La belleza de las linters en JavaScript es que se
pueden poner en un enlace de precompromiso . Personalmente, no me gusta que Prettier no almacene el historial (aunque, por otro lado, tampoco acumula deuda técnica). Desde el punto de vista del análisis estadístico del código, tales pruebas son miserables, porque no se puede ver la dinámica del proyecto, ver cuántos errores hubo ayer, anteayer. En principio, este problema se resuelve en
SonarQube , también está en la solución en la nube. Este es un analizador de código estadístico que almacena el historial de ejecuciones, puede funcionar con dos docenas de lenguajes, incluso PHP (¿quién más no necesita la mano de hierro del análisis estático? :)). En él, puede observar la dinámica de sus vulnerabilidades, errores, deudas técnicas y más.
Pruebas de complejidad
Los frontenders usan
linters porque quieren una hermosa sangría . La complejidad también es una prueba de software con la que puede intentar verificar la calidad de su código.
La imagen muestra cómo estoy tratando de seguir el pensamiento del joven que hizo la solicitud de extracción. En tales casos, sugiero demoler todo y construir un camino directo.Las pruebas de complejidad siguen un principio muy simple: calculan la
complejidad ciclomática del algoritmo. Por ejemplo, leen una función, encuentran 10 variables en ella. Si es 10, probablemente sea difícil. Establezcamos la complejidad 1. Para cada ciclo daremos 3 puntos, para un ciclo en un ciclo - 9, para cada ciclo en un ciclo de un ciclo - 27. Agregamos todo y decimos: complejidad ciclomática 120, y una persona puede entender solo 6. El significado de esta evaluación es para decir subjetivamente cuándo necesita refactorizar su código fuente, dividirlo en pedazos, resaltar nuevas funciones y similares. Y sí, SonarQube también puede hacerlo.
Pruebas alternativas
En mi mundo, las pruebas alternativas también se aplican a las pruebas suaves.
La solidaridad es algo muy útil para la incorporación. Y no solo para front-end, aunque está escrito en JavaScript. Le permite
probar el entorno de trabajo . Anteriormente, era necesario componer enormes instrucciones, indicando las versiones del lenguaje de programación, las bibliotecas, la lista de software necesario para comenzar, mantener todo actualizado, etc. Ahora podemos decir esto: “Aquí está su computadora, aquí está el código fuente. Hasta que
Solidaridad pase , no vengas ”. Al mismo tiempo, el umbral de entrada es bajo. Solidarity puede tomar huellas digitales de un entorno de trabajo personalizado y le permite agregar reglas muy simples para validar no solo el software instalado. Y te enfurece cuando se les ocurren las palabras: "Oh, lo siento, algo no funciona para mí allí, ¿puedes ayudarme? ..."
El segundo caso de uso (es el principal) es
probar el entorno de producción con las palabras: "Las pruebas unitarias para CI ciertamente pasaron, pero las configuraciones de CI y PROD varían significativamente. Entonces no hay garantías ... ". El objetivo de la biblioteca es muy simple: cumplir la primera regla de integración continua: "todos deberían tener el mismo entorno". Limpio, aislado para que no haya efectos secundarios, bueno, o al menos para que haya menos ... ¿a quién estoy tratando de engañar?
Llamada API
Sucede que los desarrolladores se dividen en varios equipos: algunos escriben una interfaz, otros escriben un back-end. Una situación ficticia que no puede estar en un equipo real: todo funcionó ayer, y hoy después de dos lanzamientos, adelante y atrás, todo se rompió. ¿Quién tiene la culpa? Yo, como persona con experiencia inicial en backend, siempre digo: front-end.
Es simple, se equivocaron en alguna parte, como siempre. En un momento, los proveedores de servicios de fondo vienen y dicen: “Aquí leemos la publicación por
referencia , revisamos la guía y
aprendimos cómo emitir su API REST . Y no creerás que ha cambiado ... " En general, si sus backends no son amigos de Swagger, openAPI u otras soluciones similares, vale la pena tomar nota.
Rendimiento js
Y finalmente, la prueba de rendimiento JS.
Nadie está probando el rendimiento JS excepto los fabricantes de navegadores. Como regla, todos usan
Benchmark.js . "Ah, y hemos impulsado nuestro Explorer durante 18 años para que muestre una tableta de mil millones por mil millones más rápido que en Chrome". ¿Quién necesita una tableta así?
Si desea realizar pruebas de rendimiento, es mejor ir a la inversa: pruebe de extremo a extremo y vea cómo funciona. El usuario forma una percepción de cómo funciona la aplicación en su conjunto, al
usuario no le importa que estos sean problemas en el lado del servidor.Historia de guerra # 1
Ahora un ejemplo de la vida. De alguna manera, los jefes se nos acercan y nos dicen: “Tu frente funciona muy mal, apenas se está cargando. Hay que hacer algo con el rendimiento, la gente se queja ". Pensamos: ahora tendremos dos semanas para realizar ajustes, recogiendo registros,
¿por qué actualizaste , recogiendo la sacudida de un árbol, cortando todo en pedazos con carga dinámica ... ¿Qué pasa si no se quema, lo dividiremos o empeoraremos? Se necesita hacer una solución alternativa.
Abrimos el navegador, mira: 7,5 MB, 2 segundos, todo está bien.
Pon Nginx GZip:

Nginx tiene la capacidad de ajustar la relación de compresión, intentemos:

Crecimiento: 25% de la productividad. Para temprano Echa un vistazo al pequeño logotipo del diseñador en la esquina. Sigue siendo muy hermoso, incluso si se estira, pero ¿por qué lo necesitamos?

Esto es lo que obtuvo después de optimizar una imagen. Puede evaluar el peso del logotipo usted mismo. Finalmente, llegamos al cliente y le decimos: “la primera descarga no es tan importante como la segunda. Y, habilite el almacenamiento en caché forzado:

... ¡Todos están felices, todos están jubilosos! " Excepto por el usuario, por supuesto.
Como resultado, decidimos realizar una auditoría de tamaño con mayor frecuencia.
Gzip, fuentes, imágenes, estilos : esos lugares donde casi nadie mira, pero hay muchos beneficios.
Madge y UpdtrJS
Siguiente paso:
auditoría de dependencia .
Madge es tal cosa que analiza el código y dice: aquí hay una clase más o menos asociada con tal y tal, etc. Si todo pasa por un componente y se rompe, habrá poco placer. Madge es una gran herramienta de visualización, pero adecuada solo para exploración manual. Tiene una opción, circular, que busca todas las
dependencias cíclicas en su proyecto . Si hay alguno, es malo, si no, entonces todavía no han escrito.
El dolor de los marcos y bibliotecas heredados casi se resuelve con
updtrJS .
¿Tienes 70 mil líneas de código? ¿Estás tratando de pasar del 13º React al 16º?
Updtr no lo ayudará a moverse, pero le ayudará a determinar a qué versiones de bibliotecas puede mudarse sin
consecuencias graves . Además, permite a los desarrolladores mantenerse en tendencia, ayuda a mantener las dependencias actualizadas. Si tiene una buena cobertura de prueba, lo recomiendo.
Tipos estáticos
Utilice la escritura estática JS , porque la escritura dinámica
JS no es una característica en absoluto, es un abismo. Flow, typeScript, reasonML eso es todo ...
Prueba de caos
La esencia de las pruebas de caos es simple: inicie el navegador y comience a pinchar todo lo que se pincha, ingrese todo lo que se ingresó en todos los campos, y así sucesivamente. Hasta que se rompa. Amazon, exceptions . , « , ». , . :
Gremlin.com Chaos Monkey .
React — 16- , componentDidCatch. , exception, . .
Naughty String , , . , « », internal server error, ?

War Story #2
– . . def generateRandomPostfix(String prefix) { return prefix + "-" + Math.abs(new Random().nextInt()).toString() } def "testCorrectRandomPostfix"(){ given: def prefix = "ASD" when: def result = generateRandomPostfix(prefix) then: result?.matches(/[a-zA-Z]++-[0-9]++/) }
. . , .
. ASD, , . . ( , groovy). Java integer 1 integer. integer integer — .


, . . ? «+» fromX. , - , XML .
.
.
TestCheck.JS — . , , . —
Flow-To-Gen : Flow , .
check( property( gen.int, gen.int, (a, b) => a + b >= a && a + b >= b ) )
{ result: false, failingSize: 2, numTests: 3, fail: [ 2, -1 ], shrunk: { totalNodesVisited: 2, depth: 1, result: false, smallest: [ 0, -1 ] } }
TestCheck « », . , . , , : 0 -1. 2 -1, , . !
. . , . , ? -? permission'? ? , , , , .
3rd party failures . „ “ — .
Production-
production 16- React :
ErrorCeption HoneyBadger (
Sentry ). ,
.Optimizely /-. , , , , , .
Out of the box
JS — . , JavaScript. —
validator.w3.org/checklink . , , , , .
, , .
Achecker.ca/checker .
Webpagetest.org — , .
Tools.pingdom.com — .
www.w3.org/WAI/ER/tools .
. . , Jenkins Multibranch Plugin, - -. -, (« , »), nightly-, - regress, full regress, smart regress ..
— , , , , . , , , .
, , . .