tutorial test run princess princesa karma unit-testing integration-testing jasmine

unit testing - test - ¿Las pruebas atómicas tienen sentido en entornos creados dinámicamente?



karma jasmine (2)

Estamos construyendo un producto que permite a los usuarios crear bases de datos personalizadas y almacenar datos dentro de esos DB (WebApp).

Nuestro problema para probar el frontend (coffeescript) es que cada prueba debe ser atómica, pero eso requeriría configurar un DB para ver si un elemento dentro de ese DB puede crearse y persistir o para ver cómo los cambios en un DB afectan los elementos.

Básicamente, el problema es que el código de configuración necesario para llegar a las pruebas de elementos básicamente configura un nuevo DB y, por lo tanto, es igual al código que prueba la configuración de un nuevo DB.

Hay dos enfoques y nos damos cuenta de que usar:

1) Crear y derribar un nuevo DB con cada grupo de pruebas

  • (+) Sorta Atomic (sigue fallando si falla la configuración de un DB)
  • (-) Toma mucho tiempo para ejecutar
  • (-) Toneladas de código de conexión
  • (-) No hay forma de explorar el entorno creado
  • (-) Desordenado en los errores, todo falla

2) Haga la configuración paso a paso como pruebas separadas, una para la otra, rutina de limpieza al comienzo de una prueba

  • (+) Se puede acceder al entorno creado a través de la interfaz de usuario (no se destruye automáticamente)
  • (+) Prueba paso a paso, menos código general / repetitivo
  • (-) Las pruebas dependían unas de otras (desordenadas)
  • (-) Algo desordenado en general

Por lo tanto, nos preguntamos si la regla de oro de que las pruebas deben ser atómicas tiene sentido en un entorno tan dinámico.


No estás haciendo pruebas atómicas si estás hablando con una base de datos.

Necesita burlarse de la interfaz de la base de datos y hablar con el simulacro en su lugar. Eso será rápido, y podrá usar el simulacro para introducir errores que serían difíciles usando la base de datos real.


Básicamente, de lo que estás hablando es de pruebas de integración. Estos son diferentes de las Pruebas Unitarias. Ejemplos de prueba de integración serían las pruebas UI automatizadas o las pruebas UI codificadas. En la mayoría de los proyectos en los que he trabajado, hemos tenido ambos tipos de pruebas y te recomiendo encarecidamente que tengas ambos tipos en tu proyecto también.

La filosofía detrás de estas dos pruebas es ligeramente diferente.

  • Las pruebas unitarias están destinadas a probar bits aislados de funcionalidad.
  • Están destinados a ser muy rápidos.
  • Un desarrollador debe poder ejecutarlos todos en su máquina en un tiempo razonable.

Hay varias consecuencias de esta filosofía.

  • Debido a que la prueba unitaria está probando un poco de funcionalidad aislada, debe usar simulaciones y resúmenes para aislar el resto del entorno y concentrarse solo en pequeños bits de funcionalidad.
  • El aislamiento ayuda a su "pensamiento de diseño" al escribir estas pruebas. De hecho, esta es la razón por la cual se requieren pruebas unitarias para ser rápidas, porque un desarrollador está activa y constantemente cambiando el código y las pruebas unitarias como parte del proceso de diseño y rediseño. Debe haber muy poca sobrecarga para configurar, cambiar y ejecutar las pruebas unitarias. Debería ser capaz de ignorar todo lo que no sea el problema que estoy tratando de resolver e iterar rápidamente y reiterar mis diseños y pruebas. Esta es la idea detrás de TDD y su afirmación de ayudar a escribir un buen código comprobable. Si pasa mucho tiempo intentando establecer una prueba unitaria demasiado compleja, entonces debe comenzar a reconsiderar su diseño.
  • La naturaleza rápida significa que puede ejecutar estos como parte de su compilación de Integración Continua.
  • La desventaja es que, debido a que está probando cada funcionalidad de forma aislada, no sabe si funcionarán juntas como un todo. Cada vez que escribe un simulacro, está implícitamente preparando una suposición sobre cómo funciona el resto del sistema y que el resto del sistema funciona actualmente como debe (es decir, nada más está roto como parte de su implementación o ejecución). o parcheo del sistema operativo, etc.)

Las pruebas de integración están destinadas a probar la funcionalidad de extremo a extremo. Intenta NO burlar o aislar ninguna parte del sistema.

De nuevo hay varias consecuencias de esta filosofía. Tenga en cuenta que no es necesario que las pruebas de integración sean rápidas.

  • Las pruebas de integración, por su propia naturaleza, deben ejecutarse después de su implementación completa (a diferencia de las pruebas unitarias que se pueden ejecutar tan pronto como se compile el código).
  • Debido a que demoran más, no los ejecuta como parte de su entorno de CI, pero aún necesita ejecutarlos regularmente. Por lo general, los ejecutamos como parte de nuestras versiones nocturnas. O puede ejecutarlo dos veces al día, etc.
  • Debido a que las pruebas de integración adoptan un enfoque de caja negra para todo el sistema, realmente no lo ayuda con su "pensamiento de diseño" sobre cómo construir realmente el sistema. Pero ayuda a pensar en las especificaciones del sistema como un todo. es decir, qué debería hacer el sistema, no cómo debería hacer algo.

Tenga en cuenta que en ambos casos la regla de las pruebas siendo atómica todavía se aplica. Cada prueba es diferente de otras pruebas. De esta forma, cuando falla una prueba, puede estar seguro de todas las condiciones que causan su falla y concentrarse solo en solucionarlo. Es solo que una prueba de integración toca tantas partes de su sistema como sea posible.

Para darte un ejemplo de nuestro proyecto actual.

Digamos que necesitamos escribir un poco de funcionalidad que requiera que agreguemos una nueva tabla a la base de datos y la pasemos por todas las capas para mostrarla en la interfaz de usuario.

Comenzamos creando nuestras clases de lógica de negocios, clases de dominio, redactamos el servicio web apropiado, construimos modelos de vista, modificamos la base de datos, etc. Mientras hacemos cada uno de ellos, escribimos pruebas unitarias para probar el código que estamos escribiendo actualmente. Entonces cuando construimos las clases de lógica de negocios, nos burlamos de todo lo demás para asegurarnos de que la lógica en la clase sea válida (por ejemplo, los clientes mayores de 60 años obtienen un 50% de descuento en su seguro de automóvil, etc.)

Una vez que hacemos eso, ahora necesitamos actualizar nuestros scripts / paquetes de implementación, etc. para poder implementarlo. es decir, actualice los scripts SQL de creación de bases de datos y los scripts SQL de alteración de la base de datos, etc. (En su caso, este será un proceso complejo).

Ahora escribimos pruebas de integración. En este caso, podríamos probar desde SQL Server a Web Service. Hay una clase base de prueba de integración SQL que contiene el método de configuración y eliminación para cada prueba. En la configuración, creamos una nueva base de datos utilizando nuestros scripts de implementación de sql. Cada prueba también especifica un script sql de datos de prueba. Entonces, por ejemplo, este script de datos de prueba podría insertar un nuevo registro en la tabla del cliente, cuya edad es de 70 años. Ejecutamos este script como parte del "Arreglo" de nuestra prueba. A continuación, realice una llamada al servicio web para buscar clientes mayores de 60 años. Esta es la parte "Act" de la prueba y, a partir del resultado, verificamos que solo recuperemos al usuario que insertamos en la base de datos. Al final de la prueba, la base de datos se elimina. Hemos detectado errores aquí cuando las columnas en la base de datos SQL no son nulables o las columnas de fecha y hora se desbordan porque la fecha de tiempo mínima predeterminada en .Net es de un tamaño diferente al de la fecha y hora mínima del servidor SQL.

Algunas funcionalidades requieren que interactuemos con una base de datos Oracle. Por ejemplo, si se agrega un nuevo registro a Oracle, se inicia un procedimiento trigger / db que transfiere ese registro a SQL y luego tenemos que subirlo a las capas. En este caso, tenemos una clase base de prueba de integración OracleSQL. Como habrás adivinado, esto sigue un patrón similar, pero crea dbs de Oracle y SQL inserta datos de prueba en Oracle y los destruye a ambos al final de la prueba.

Los desarrolladores suelen elegir la capa de servicio web para escribir sus pruebas de integración. Los evaluadores, por otro lado, usan herramientas de automatización de la interfaz de usuario para asegurarse de que los datos realmente se muestran en la pantalla. Por ejemplo, registrarán una prueba que vaya a la página web, haga clic en el botón de búsqueda, ponga "60" en el cuadro de edad, haga clic en el botón de búsqueda, etc. Esa prueba podría aprovechar el mismo script de datos de prueba sql que inserta datos de prueba que el desarrollador escribió (o el equipo de pruebas podría acudir al desarrollador y pedir ayuda para crear scripts de SQL para insertar los datos tan intrincados que se le ocurran). Pero el punto es que una vez que se crea el script de inserción de datos de prueba, aprovecha el mismo sistema subyacente para eliminar toda la base de datos, crear una nueva, insertar datos de prueba y ejecutar la prueba especificada.

Entonces, para responder a su pregunta, necesitará dos tipos de pruebas, pruebas unitarias y pruebas de integración. Es posible que tenga que realizar un trabajo inicial para crear algunas clases base o métodos auxiliares para crear / eliminar bases de datos, automatizar su implementación para instalar / desinstalar otros componentes de su sistema, etc. De todos modos, tendrá que hacer esto para su implementación final. Las pruebas de integración también estarán estrechamente relacionadas y dependerán de su estrategia de implementación. Esto es una ventaja y no una desventaja en mi opinión. Si bien puede ser doloroso al principio configurarlo todo, una de las cosas que tus pruebas de integración están probando implícitamente es tu mecanismo de implementación. Si hay algún problema con la implementación / instalación de cualquiera de los componentes requeridos por su sistema, desea conocerlo lo más rápido posible. No es el día antes de que se supone que se está desplegando en la producción.

Un buen conjunto de pruebas es invaluable. También debe ser aislado, riguroso y completo. Las pruebas no deberían fallar cuando no las necesitan, pero lo más importante es que deberían fallar cuando lo necesiten. Y cuando fallan, desea que brinden la mayor cantidad de información posible y que lo dirijan a la ubicación exacta de la falla. Esto hace que solucionar el problema sea una tarea mucho más fácil. Cada vez que ingrese a la construcción de este conjunto de pruebas pagará por sí mismo en poco tiempo.