La imagen general de las pruebas unitarias



Esta no es una guía sobre qué caracteres necesita ingresar en el editor de código para obtener pruebas unitarias. Esto es alimento para la mente, que debe consumirse antes de tomar estas acciones.

El tema de las pruebas unitarias no es tan simple como parece. Muchos de nosotros, los desarrolladores, llegamos a las pruebas unitarias bajo la presión de clientes, empleados, colegas, sus ídolos, etc. Comprendemos rápidamente su valor y, una vez finalizados los preparativos técnicos, nos olvidamos de la imagen general, si es que la entendimos alguna vez. En este artículo, hablaré brevemente sobre qué son las pruebas unitarias y qué no, tanto en general como en PHP, y al mismo tiempo describiré qué lugar ocupan las pruebas unitarias en la esfera de control de calidad.

¿Qué es la prueba?


Antes de profundizar en las pruebas unitarias, debe estudiar la teoría de las pruebas en sí para no cometer errores como los cometidos por los autores de uno de los marcos PHP más populares: mostraron pruebas de integración en su sitio web y las llamaron pruebas unitarias. No, Laravel, estas no son pruebas unitarias. Aunque esto no me impide seguir amando este marco.

Las pruebas de software se definen como "una investigación realizada para proporcionar a las partes interesadas información sobre la calidad del producto". Esto se opone a "las pruebas de software son un desperdicio del presupuesto del proyecto por parte de desarrolladores que no hacen nada importante y luego piden más tiempo y dinero, porque" nada "puede ser muy costoso". Nada nuevo aquí.

Aquí está mi breve historia de convertirse en una prueba:

  • 1822 - Motor de diferencia (Charles Babbage).
  • 1843 - Motor analítico (Ada Lovelace).
  • 1878 - Edison introduce el término "error".
  • 1957 - Pruebas y depuración de programas (Charles Baker).
  • 1958 - El primer equipo de prueba de software (Gerald Weinberg).
  • 1968 - Crisis PO (Friedrich Bauer).
  • Década de 1970 - Modelo de cascada, modelo relacional, descomposición, análisis crítico ( Tutorial ), diseño e inspección de código, calidad y métricas, patrones de diseño.
  • Década de 1980: análisis CRUD, arquitectura del sistema, autotesting, modelo V, confiabilidad, costo de calidad, métodos de uso, patrones de diseño de OOP.
  • Década de 1990: Scrum, pruebas de usabilidad, MoSCoW, pruebas heurísticas, automatización de software y pruebas.

Si te relacionas con una generación de millennials como yo, te sorprenderá que los equipos de prueba existieran MUCHO antes de que nacieras. Detente por un momento, inhala, exhala, cálmate.
La historia muestra cómo el tipo de prueba que se consideró "suficientemente buena" para las partes interesadas cambió con el tiempo. Fases aproximadas que fueron guiadas durante las pruebas:

  • ... - 1956 depuración
  • Demostración de 1957-1978
  • 1979-1982 destrucción
  • Estimación 1983 - 1987
  • 1988 - ... prevención

Por lo tanto, las pruebas unitarias son necesarias para evitar discrepancias entre el proyecto y la implementación.

¿Qué es realmente probar?


Existen diferentes clasificaciones de pruebas de software. Para comprender mejor el lugar de las pruebas unitarias, solo mencionaré los enfoques más extendidos.

Las pruebas son: estática y dinámica, "caja" (caja blanca, caja negra, caja gris), niveles y tipos. Cada enfoque utiliza diferentes criterios de clasificación.

Pruebas estáticas y dinámicas.


Las pruebas estáticas se realizan sin ejecución de código. Esto incluye corrección de pruebas, verificación, revisión de código (al observar el trabajo de otra programación / par), análisis crítico, inspecciones, etc.

Las pruebas dinámicas para obtener los resultados correctos requieren la ejecución del código. Por ejemplo, para pruebas unitarias , integración, sistema, aceptación y otras pruebas. Es decir, las pruebas se llevan a cabo utilizando datos dinámicos, entradas y salidas.

Enfoque de caja


Según este enfoque, todas las pruebas de software se dividen en tres tipos de cuadros:

  • La prueba de recuadro blanco verifica las estructuras y módulos internos, ignora la funcionalidad esperada para los usuarios finales. Estas pueden ser pruebas API, inyección de fallas, pruebas unitarias , pruebas de integración.
  • Las pruebas de caja negra están más interesadas en lo que hace el software y no en cómo lo hace. Esto significa que los probadores no están obligados a comprender el objeto de prueba o comprender cómo funciona bajo el capó. Este tipo de pruebas está dirigido a usuarios finales, su experiencia interactuando con una interfaz visible. Las cajas negras incluyen pruebas basadas en modelos, pruebas de uso, tablas de transición de estado, pruebas de especificación, etc.
  • Las pruebas del tipo de " caja gris " están diseñadas con conocimiento de algoritmos de software y estructuras de datos (caja blanca), pero se realizan a nivel del usuario (caja negra). Esto incluye pruebas de regresión y pruebas de patrones.

Ahora, para confundirlo, diré que las pruebas unitarias también pueden aplicarse al "recuadro negro", ya que puede comprender el módulo que se está probando, pero no todo el sistema. Aunque para mí sigue siendo una "caja blanca", y sugiero que esté de acuerdo con esto.

Niveles de prueba


Su número varía, generalmente en el rango de 4 a 6, y todos son útiles. Los nombres también pueden ser diferentes, dependiendo de la cultura adoptada por la empresa, puede conocer las pruebas de "integración" como "funcionales", las pruebas de "sistema" como "automatizadas", y así sucesivamente. Por simplicidad, describiré 5 niveles:

  1. Prueba unitaria
  2. Pruebas de integración.
  3. Prueba de interfaces de componentes.
  4. Prueba de sistema.
  5. Pruebas de aceptación operacional.

Las pruebas unitarias prueban la funcionalidad de un código en particular, generalmente una función a la vez. Las pruebas de integración verifican las interfaces entre los componentes para que los módulos ensamblados juntos formen un sistema que funcione según lo previsto. Este es un punto importante, porque una gran cantidad de pruebas, que se llaman pruebas unitarias, en realidad son pruebas de integración, y los desarrolladores las consideran módulos. Si tiene la intención de usar varios módulos, esto es probar la integración entre ellos, no los módulos en sí. La prueba de interfaces de componentes verifica los datos transferidos entre diferentes módulos. Por ejemplo, recibimos datos del módulo 1 - verificado - transferido al módulo 2 - verificado. La prueba del sistema es una prueba de extremo a extremo para verificar el cumplimiento de todos los requisitos. La prueba de aceptación operacional se realiza para verificar la preparación operacional. No es funcional, solo se verifica la capacidad de servicio de los servicios, si algún subsistema daña el medio ambiente y otros servicios.

Tipos de pruebas


Cada tipo de prueba, independientemente de su nivel, también se puede dividir en otros tipos. Hay más de 20 tipos comunes. Los mas comunes:

  • Pruebas de regresión .
  • Pruebas de aceptación.
  • Prueba de humo
  • Uat
  • Pruebas destructivas .
  • Pruebas de rendimiento.
  • Pruebas continuas .
  • Pruebas de usabilidad.
  • Pruebas de seguridad.

Por el nombre, está claro por qué se pretende este o aquel tipo de prueba. Negrita son las pruebas unitarias en PHP. Si realmente lo desea, puede aplicar cada uno de estos términos a las pruebas unitarias. Sin embargo, la principal variedad de pruebas unitarias son las pruebas de regresión, que verifican si todos los módulos del sistema se ejecutan correctamente después de realizar cambios en el código.

Ahora sabe que las pruebas unitarias son dinámicas, pertenecen a la clase de "recuadro blanco", se realizan a nivel de módulo, son pruebas de regresión, pero las pruebas modulares se pueden entender como muchos tipos de pruebas. Entonces, ¿qué son realmente las pruebas unitarias?

¿Qué es la prueba unitaria?


Un modelo V es una representación gráfica de los niveles, tipos y su propósito en el ciclo de vida de desarrollo de software.



Después de verificar y aprobar los requisitos detallados para el producto, cuando comenzaron a escribir el código, las pruebas unitarias se convierten en la primera línea de defensa contra cualquier inconsistencia. Por lo tanto, las empresas que entienden lo que están haciendo están obligando a los desarrolladores a usar pruebas unitarias o incluso TDD, ya que es mucho más barato corregir errores en las etapas iniciales que en las posteriores.

Y esto es justo. Las pruebas unitarias tienen muchas ventajas. Ellos son:

  • Aísle cada parte del programa y verifique su corrección.
  • Ayuda a detectar problemas temprano.
  • Hacen que los desarrolladores piensen en términos de entrada, salida y condiciones erróneas.
  • Le dan al código una apariencia conveniente para las pruebas, facilitan la refactorización futura.
  • Simplifique la integración de módulos de trabajo (!).
  • Reemplazar parcialmente la documentación técnica.
  • Obligado a separar la interfaz de la implementación.
  • Demuestran que el código del módulo funciona como se esperaba (al menos matemáticamente).
  • Se puede utilizar como conjuntos de pruebas de regresión de bajo nivel.
  • Demostrar progreso en la integración incompleta del sistema.
  • Reduzca el costo de corregir errores (con TDD, aún más).
  • Permiten mejorar la arquitectura de la aplicación determinando la responsabilidad de los módulos.
  • Si puede probarlo, puede conectarlo a su sistema.
  • ¡Las pruebas unitarias son DIVERTIDAS!

Sin embargo, hay ciertas limitaciones en las que pensó, probablemente al leer esta lista:

  • Las pruebas unitarias no detectan errores de integración.
  • Cada expresión booleana requiere al menos dos pruebas, y el número crece rápidamente.
  • Las pruebas unitarias son tan defectuosas como el código que prueban.
  • Vincular las pruebas a un par de marcos o bibliotecas específicas puede limitar el flujo de trabajo.
  • La mayoría de las pruebas se escriben después de que se completa el desarrollo. Es triste Use TDD!
  • Quizás después de un poco de refactorización, el sistema funcionará como antes, pero las pruebas fallarán.
  • El costo del desarrollo está creciendo.
  • Error humano: comentando sobre pruebas rotas.
  • Error humano: agregar soluciones al código específicamente para pasar las pruebas unitarias.

Este último me mata más. (Casi) en cada proyecto, justo en el código fuente de la aplicación de trabajo, encuentro líneas como "si es una prueba unitaria, cargue una base de datos SQLite sustituta, de lo contrario cargue otra base de datos" o "si es una prueba unitaria, no envíe un correo electrónico, de lo contrario enviar ", y así sucesivamente. Si su aplicación tiene una arquitectura pobre, no pretenda que puede arreglar un software pésimo con un buen pase de prueba, esto no mejorará.

A menudo discutía con colegas y clientes qué es una buena prueba de unidad. El:

  • Rápido
  • Automatizado.
  • Controla completamente todas sus dependencias.
  • Fiable: se puede iniciar en cualquier orden, independientemente de otras pruebas.
  • Solo se puede ejecutar en la memoria (sin interacciones con la base de datos, lecturas / escrituras en el sistema de archivos).
  • Siempre devuelve un único resultado.
  • Conveniente para la lectura y el acompañamiento.
  • No prueba la configuración SUT (sistema bajo prueba).
  • Tiene una TAREA ÚNICA claramente definida.
  • Está bien nombrado (y es lo suficientemente comprensible para evitar la depuración solo por descubrir qué está fallando).

Para aquellos que sonrieron después de leer "automatizado": no quise integrar PHPUnit o JUnit en las canalizaciones de CI. El punto es que si cambia el código, guárdelo y no sepa si los módulos pasan sus pruebas, entonces no están automatizados, pero deberían. La opción ganadora es el seguimiento de archivos.

¿Qué se debe someter a pruebas unitarias?


En sistemas normales, las pruebas unitarias deben escribirse para:

  • Módulos: partes aisladas indivisibles del sistema que realizan cualquier tarea (función, método, clase).
  • Métodos públicos
  • Métodos protegidos, pero solo en casos raros y cuando nadie los ve.
  • Errores y sus soluciones.

La definición de una prueba unitaria depende del desarrollador que escribió el código. En PHP, casi siempre es un método o función de clase, porque es un software indivisible que tiene sentido por sí solo . Varias veces vi cómo los desarrolladores usaban una variedad de miniclases de un método como un solo módulo. Esto tiene sentido si la funcionalidad mínima requiere múltiples objetos.

Entonces usted mismo puede determinar qué es un módulo para usted. O puede probar los métodos uno por uno, haciendo la vida más fácil para ese tipo que luego trabajará con el código.

Si no realiza pruebas unitarias, le propongo que haga esto después del próximo gran error. Verifique con qué método estará asociado, escriba una prueba fallida con los argumentos y resultados correctos, corrija el error, ejecute la prueba de la unidad nuevamente. Si se pasa, puede estar seguro de que este error tuvo que repararse por última vez (teniendo en cuenta sus escenarios de entrada específicos).

Este enfoque hace que las pruebas unitarias sean más fáciles de entender. Analice cada método por separado. Los proveedores de datos pueden ayudar a determinar la entrada y la salida para cualquier escenario que se le ocurra, por lo que pase lo que pase, sabrá qué esperar.

Lo que NO necesita ser probado


Es un poco más difícil determinar que no es necesario realizar la prueba. Traté de compilar una lista de elementos que no necesitan ser sometidos a pruebas unitarias:

  • Funcionalidad fuera del alcance de los módulos (!)
  • Integración de módulos con otros módulos (!)
  • Comportamiento no aislado (dependencias no desmontables, bases de datos reales, red)
  • Métodos privados y seguros.
  • Métodos estáticos.
  • Bibliotecas externas
  • Tu marco

Estoy seguro de que las pruebas unitarias no deben aplicarse a ninguno de los anteriores, excepto los métodos estáticos. Me gusta argumentar que estático, en esencia, significa procedimiento, y en muchos casos el procedimiento es global. Si el método estático llama a otro método estático, entonces esta dependencia no se puede anular. Esto significa que ahora está probando de forma aislada. Y entonces esto ya no es una prueba unitaria. Por otro lado, esta es la parte del código que puede vivir por sí misma, tiene un propósito y debe probarse para asegurarse de que no importa qué parte de este estúpido sistema llame la parte probada del código, no se romperá. Por lo tanto, creo que puede probar métodos estáticos si está seguro de que el resultado de su prueba no puede ser modificado por ninguna otra prueba, y que el lenguaje o el marco de trabajo le permite probar de forma nativa.

¿Cómo escribir pruebas unitarias?


  • Escriba el código adecuado para las pruebas unitarias, luego pruébelo.
  • Escriba el código adecuado para las pruebas unitarias, luego pruébelo.
  • Escriba el código adecuado para las pruebas unitarias, luego pruébelo.

Si "probarlo" no es suficiente, entonces laracasts.com tiene muy buenos videos sobre pruebas de unidad PHP. Hay muchos sitios dedicados a la misma tarea en otros idiomas. No veo ninguna razón para explicar cómo hago las pruebas unitarias, porque las herramientas cambian bastante rápido, y cuando lees este texto, puedo cambiar de PHPUnit a Kahlan. O no Quien sabe

Pero responder la primera pregunta (cómo escribir código adecuado para pruebas unitarias) es mucho más fácil y es poco probable que la situación cambie mucho con el tiempo:

  • SÓLIDO
  • SECO
  • La falta de nuevas palabras clave en el constructor.
  • La ausencia de bucles en el constructor (y las transiciones, si se especifica).
  • Falta de métodos estáticos, parámetros, clases.
  • Falta de métodos setup (): los objetos deben inicializarse completamente después de la construcción.
  • La falta de singleton (estado global) y otros antipatterns no comprobables.
  • La falta de objetos omnipotentes (objetos de Dios).
  • Falta de clases con funcionalidad mixta (clases mixtas de interés).
  • Sin dependencias ocultas.

Ahora, sabiendo cuáles son las pruebas unitarias y cuáles no, qué necesita y qué no necesita probar, qué lugar ocupan las pruebas unitarias en el ciclo de vida del desarrollo de software, será más fácil implementarlas. Queda por encontrar un marco o biblioteca a su gusto. En caso de duda, tome el marco / lenguaje estándar de facto.

En conclusión: las pruebas unitarias son muy importantes tanto para desarrolladores como para empresas. Deben escribirse, existen métodos probados que lo ayudarán a cubrir fácilmente los módulos con pruebas, principalmente preparando los módulos ellos mismos. Pero todas estas técnicas no tienen sentido sin el conocimiento de la teoría de prueba descrita en este artículo. Debe poder distinguir las pruebas unitarias de las pruebas de otros tipos. Y cuando tenga una comprensión clara en su cabeza, le resultará mucho más fácil escribir exámenes.

Source: https://habr.com/ru/post/es412695/


All Articles