¿Son malas las pruebas de integración (de la base de datos)?

120

Algunas personas sostienen que las pruebas de integración son malas y están mal : todo debe ser probado por la unidad, lo que significa que debes burlarte de las dependencias; una opción que, por varias razones, no siempre me gusta.

Creo que, en algunos casos, una prueba unitaria simplemente no prueba nada.

Tomemos como ejemplo la siguiente implementación de repositorio (trivial, ingenua) (en PHP):

class ProductRepository
{
    private $db;

    public function __construct(ConnectionInterface $db) {
        $this->db = $db;
    }

    public function findByKeyword($keyword) {
        // this might have a query builder, keyword processing, etc. - this is
        // a totally naive example just to illustrate the DB dependency, mkay?

        return $this->db->fetch("SELECT * FROM products p"
            . " WHERE p.name LIKE :keyword", ['keyword' => $keyword]);
    }
}

Digamos que quiero demostrar en una prueba que este repositorio realmente puede encontrar productos que coincidan con varias palabras clave dadas.

A falta de pruebas de integración con un objeto de conexión real, ¿cómo puedo saber que esto realmente está generando consultas reales y que esas consultas realmente hacen lo que creo que hacen?

Si tengo que burlarme del objeto de conexión en una prueba unitaria, solo puedo probar cosas como "genera la consulta esperada", pero eso no significa que realmente vaya a funcionar ... es decir, tal vez esté generando la consulta Lo esperaba, pero tal vez esa consulta no hace lo que creo que hace.

En otras palabras, siento que una prueba que hace afirmaciones sobre la consulta generada, esencialmente no tiene valor, porque está probando cómo findByKeyword()se implementó el método , pero eso no prueba que realmente funcione .

Este problema no se limita a los repositorios o la integración de la base de datos: parece aplicarse en muchos casos, donde hacer afirmaciones sobre el uso de un simulacro (prueba doble) solo prueba cómo se implementan las cosas, no si van a En realidad funciona.

¿Cómo lidias con situaciones como estas?

¿Las pruebas de integración son realmente "malas" en un caso como este?

Entiendo que es mejor probar una cosa, y también entiendo por qué las pruebas de integración conducen a innumerables rutas de código, todas las cuales no se pueden probar, pero en el caso de un servicio (como un repositorio) cuyo único propósito es para interactuar con otro componente, ¿cómo puede realmente probar algo sin pruebas de integración?

mindplay.dk
fuente
55
Lea agitar.com/downloads/TheWayOfTestivus.pdf , especialmente la página 6 "La prueba es más importante que la unidad".
Doc Brown
2
@ user61852 dice "ingenuo" en la descripción, ¿sí?
mindplay.dk
44
¿Cómo estaría su compañero de trabajo absolutamente seguro de que su base de datos simulada se comporta como algo real?
Thorbjørn Ravn Andersen
12
Estás tratando de ser realista. Su compañero de trabajo está tratando de cumplir con las reglas. Siempre escriba pruebas que produzcan valor . No pierda el tiempo escribiendo pruebas que no se podrán imprimir, y no escriba pruebas que no realicen una de las siguientes acciones: aumente la probabilidad de que su código sea correcto o lo obligue a escribir más código mantenible.
jpmc26
3
@ mindplay.dk: la oración clave en ese párrafo es "Pero no te quedes atrapado en ningún dogma. Escribe la prueba que necesita ser escrita". Tu compañero de trabajo parece estar atrapado en un dogma. Y no necesitas a alguien explicando cuál es la prueba que debe escribirse en su ejemplo, ya lo sabe. Es bastante obvio que para probar si su base de datos comprende la consulta, debe ejecutar la consulta en la base de datos real , ningún simulacro puede decirle esto.
Doc Brown

Respuestas:

129

Su compañero de trabajo tiene razón en que todo lo que puede ser probado en una unidad debe ser probado en una unidad, y usted tiene razón en que las pruebas unitarias lo llevarán solo hasta cierto punto y no más allá, particularmente al escribir envoltorios simples alrededor de servicios externos complejos.

Una forma común de pensar acerca de las pruebas es como una pirámide de pruebas . Es un concepto frecuentemente conectado con Agile, y muchos han escrito sobre él, incluido Martin Fowler (quien lo atribuye a Mike Cohn en Succeeding with Agile ), Alistair Scott y el blog de Google Testing .

        /\                           --------------
       /  \        UI / End-to-End    \          /
      /----\                           \--------/
     /      \     Integration/System    \      /
    /--------\                           \----/
   /          \          Unit             \  /
  --------------                           \/
  Pyramid (good)                   Ice cream cone (bad)

La idea es que las pruebas unitarias rápidas y resistentes son la base del proceso de prueba: debe haber más pruebas unitarias enfocadas que las pruebas de sistema / integración, y más pruebas de sistema / integración que las pruebas de extremo a extremo. A medida que se acerca a la cima, las pruebas tienden a tomar más tiempo / recursos para ejecutarse, tienden a estar más frágiles y escamosas, y son menos específicas para identificar qué sistema o archivo está dañado ; naturalmente, es preferible evitar ser "muy pesado".

Hasta ese momento, las pruebas de integración no son malas , pero una gran dependencia de ellas puede indicar que no ha diseñado sus componentes individuales para que sean fáciles de probar. Recuerde, el objetivo aquí es probar que su unidad está funcionando según sus especificaciones mientras involucra un mínimo de otros sistemas rompibles : es posible que desee probar una base de datos en memoria (que cuento como una prueba de prueba de unidad doble junto con simulacros ) para pruebas de casos extremos pesados, por ejemplo, y luego escriba un par de pruebas de integración con el motor de base de datos real para establecer que los casos principales funcionan cuando se ensambla el sistema.


Como nota al margen, mencionó que los simulacros que escribe simplemente prueban cómo se implementa algo, no si funciona . Eso es algo así como un antipatrón: una prueba que es un espejo perfecto de su implementación en realidad no está probando nada en absoluto. En cambio, pruebe que cada clase o método se comporte de acuerdo con sus propias especificaciones , en cualquier nivel de abstracción o realismo que requiera.

Jeff Bowman
fuente
13
+1 para "Una prueba que es un espejo perfecto de su implementación realmente no está probando nada". Todo muy común. A esto lo llamo el antipatrón Doppelganger .
dodgethesteamroller
66
Una escuela contraria de control de calidad de software, el movimiento de pruebas basado en el contexto , se dedica en parte a disputar que existen reglas generales tan útiles como la Pirámide de Pruebas. En particular, los seminales textos del movimiento dan muchos ejemplos en los que las pruebas de integración son mucho más valiosos que otros tipos de pruebas (debido a que ponen a prueba el sistema en su contexto, como un sistema ) ....
dodgethesteamroller
77
... en consecuencia, Fowler et al., al argumentar que se debe gastar menos esfuerzo en las pruebas de integración y las pruebas de aceptación del usuario porque son demasiado difíciles de escribir de una manera sólida y sostenible, en realidad solo están proporcionando una excusa ex post facto de por qué no han descubierto cómo evaluar bien en niveles más altos.
dodgethesteamroller
1
@dodgethesteamroller Las discusiones en profundidad de una "escuela contraria" como esa probablemente sean las más adecuadas para su propia respuesta. Personalmente, considero que el blog de pruebas de Google hace un trabajo bastante bueno al describir las virtudes de las pruebas automatizadas rápidas y de alcance limitado junto con las pruebas del sistema en contexto. En caso de que no esté claro, enumero la pirámide de prueba aquí como un modelo útil o punto de partida, no como una excusa para dejar de pensar como ingeniero.
Jeff Bowman el
1
Presentación altamente recomendada sobre la jerarquía y la relación de las pruebas unitarias con las pruebas de integración: vimeo.com/80533536 Muy bien explicado.
szalski
88

Uno de mis compañeros de trabajo sostiene que las pruebas de integración son malas y están mal: todo debe ser probado por unidad,

Eso es un poco como decir que los antibióticos son malos: todo debe curarse con vitaminas.

Las pruebas unitarias no pueden atrapar todo, solo prueban cómo funciona un componente en un entorno controlado . Las pruebas de integración verifican que todo funciona en conjunto , lo cual es más difícil de hacer pero más significativo al final.

Un buen proceso de prueba integral utiliza ambos tipos de pruebas: pruebas unitarias para verificar las reglas comerciales y otras cosas que se pueden probar de forma independiente, y pruebas de integración para asegurarse de que todo funcione en conjunto.

A falta de pruebas de integración con un objeto de conexión real, ¿cómo puedo saber que esto realmente está generando consultas reales y que esas consultas realmente hacen lo que creo que hacen?

Usted podría unidad de probarlo en el nivel de base de datos . Ejecute la consulta con varios parámetros y vea si obtiene los resultados que espera. De acuerdo, significa copiar / pegar cualquier cambio en el código "verdadero". pero no le permiten probar la consulta independiente de cualesquiera otras dependencias.

D Stanley
fuente
¿No estaría probando si la base de datos contiene o no ciertos datos?
Tulains Córdova
Posiblemente, pero también podría estar probando que sus filtros, uniones complejas, etc. funcionan. La consulta de ejemplo probablemente no sea el mejor candidato para las "pruebas unitarias", pero sí una con complejas uniones y / o agregaciones.
D Stanley
Sí, el ejemplo que utilicé, como se señaló, es trivial; un repositorio real podría tener todo tipo de opciones complejas de búsqueda y clasificación, por ejemplo, utilizando un generador de consultas, etc.
mindplay.dk
2
Buena respuesta, pero agregaría que la base de datos debe estar en la memoria, para asegurarse de que las pruebas unitarias sean rápidas.
B 3овић
3
@ BЈовић: Desafortunadamente, eso no siempre es posible, ya que desafortunadamente no hay dos bases de datos compatibles y no todas funcionan en la memoria. También hay problemas de licencia para bases de datos comerciales (es posible que no tenga la licencia para ejecutarlo en ninguna máquina), ...
Matthieu M.
17

Las pruebas unitarias no detectan todos los defectos. Pero son más baratos de configurar y (re) ejecutar en comparación con otros tipos de pruebas. Las pruebas unitarias están justificadas por la combinación de valor moderado y costo bajo a moderado.

Aquí hay una tabla que muestra las tasas de detección de defectos para diferentes tipos de pruebas.

ingrese la descripción de la imagen aquí

fuente: p.470 en Code Complete 2 por McConnell

Nick Alexeev
fuente
55
Esa información fue recolectada en 1986 . Eso fue hace treinta años . Las pruebas unitarias en 1986 no eran lo que son hoy. Sería escéptico de estos datos. Sin mencionar que las pruebas unitarias detectan el error antes de que se haya cometido , por lo que es dudoso que se denuncien.
RubberDuck
3
@RubberDuck Este gráfico es de un libro de 2006, y se basa en datos de 1986, 1996, 2002 (si se mira con atención). No he examinado los protocolos de recopilación de datos en las fuentes, no puedo decir cuándo comenzaron a rastrear un defecto y cómo se informó. ¿Podría esta tabla estar algo desactualizada? Podria. El pasado diciembre estuve en un seminario, y el instructor mencionó que las pruebas de integración encuentran más errores que las pruebas unitarias (por un factor de 2, iirc). Este cuadro dice que son más o menos lo mismo.
Nick Alexeev
13

No, no son malos. Con suerte, uno debería tener pruebas de unidad e integración. Se utilizan y se ejecutan en diferentes etapas del ciclo de desarrollo.

Pruebas unitarias

Las pruebas unitarias deben ejecutarse en el servidor de compilación y localmente, después de compilar el código. Si falla alguna de las pruebas unitarias, se debe fallar la compilación o no confirmar la actualización del código hasta que se corrijan las pruebas. La razón por la que queremos aislar las pruebas unitarias es que queremos que el servidor de compilación pueda ejecutar todas las pruebas sin todas las dependencias. Entonces podríamos ejecutar la compilación sin todas las dependencias complejas requeridas y tener muchas pruebas que se ejecutan muy rápido.

Entonces, para una base de datos, uno debería tener algo como:

IRespository

List<Product> GetProducts<String Size, String Color);

Ahora, la implementación real de IRepository irá a la base de datos para obtener los productos, pero para las pruebas unitarias, uno puede burlarse de IRepository con uno falso para ejecutar todas las pruebas según sea necesario sin una base de datos actaul, ya que podemos simular todo tipo de listas de productos devuelto desde la instancia simulada y pruebe cualquier lógica de negocios con los datos simulados

Pruebas de integración

Las pruebas de integración son típicamente pruebas de cruce de límites. Queremos ejecutar estas pruebas en el servidor de implementación (el entorno real), sandbox o incluso localmente (señalado a sandbox). No se ejecutan en el servidor de compilación. Después de que el software se haya implementado en el entorno, normalmente se ejecutará como actividad posterior a la implementación. Se pueden automatizar mediante utilidades de línea de comandos. Por ejemplo, podemos ejecutar nUnit desde la línea de comandos si clasificamos todas las pruebas de integración que queremos invocar. Estos realmente llaman al repositorio real con la llamada a la base de datos real. Este tipo de pruebas ayudan con:

  • Medio ambiente Salud Estabilidad Preparación
  • Probar lo real

Estas pruebas a veces son más difíciles de ejecutar, ya que es posible que necesitemos configurarlas y / o derribarlas también. Considere agregar un producto. Probablemente deseamos agregar el producto, consultarlo para ver si se agregó y luego, una vez que hayamos terminado, eliminarlo. No queremos agregar cientos o miles de productos de "integración", por lo que se requiere una configuración adicional.

Las pruebas de integración pueden resultar muy valiosas para validar un entorno y asegurarse de que lo real funcione.

Uno debería tener ambos.

  • Ejecute las pruebas unitarias para cada compilación.
  • Ejecute las pruebas de integración para cada implementación.
Jon Raynor
fuente
Recomendaría ejecutar pruebas de integración para cada compilación, en lugar de tener que comprometerse y presionar. Depende de cuánto tiempo tarden, pero también es una buena idea mantenerlos rápidos por muchas razones.
artbristol
@ArtBristol: por lo general, nuestro servidor de compilación no tiene configurada la dependencia completa del entorno, por lo que no podemos ejecutar nuestras pruebas de integración allí. Pero si uno puede ejecutar las pruebas de integración allí, hágalo. Tenemos un entorno limitado de implementación configurado después de la compilación que usamos para las pruebas de integración para verificar la implementación. Pero cada situación es diferente.
Jon Raynor
11

Las pruebas de integración de bases de datos no son malas. Aún más, son necesarios.

Probablemente tenga su aplicación dividida en capas, y es algo bueno. Puede probar cada capa de forma aislada burlándose de las capas vecinas, y eso también es bueno. Pero no importa cuántas capas de abstracción crees, en algún momento debe haber una capa que haga el trabajo sucio, en realidad hablar con la base de datos. A menos que lo pruebe, no lo hace en absoluto. Si prueba la capa n burlándose de la capa n-1 , está evaluando la suposición de que la capa n funciona con la condición de que la capa n-1 funcione. Para que esto funcione, de alguna manera debes probar que la capa 0 funciona.

Si bien en teoría podría unir la base de datos de prueba, analizando e interpretando el SQL generado, es mucho más fácil y confiable crear una base de datos de prueba sobre la marcha y hablar con ella.

Conclusión

¿Cuál es la confianza obtenida de la unidad que prueba sus capas de repositorio abstracto , etéreo objeto-relacional-mapeador , registro activo genérico , persistencia teórica , cuando al final su SQL generado contiene un error de sintaxis?

el.pescado
fuente
Estaba pensando en agregar una respuesta similar a la tuya, ¡pero lo has dicho mejor! En mi experiencia, tener algunas pruebas en la capa que obtiene y almacena datos me ha ahorrado mucho dolor.
Daniel Hollinrake
Sin embargo, las pruebas de integración de bases de datos son malas. ¿Tiene una base de datos disponible en su línea ci / cd? Eso me parece bastante complejo. Es mucho más fácil burlarse de las cosas de la base de datos y construir una capa de abstracción para usar eso. No solo es una solución mucho más elegante, es lo más rápido posible. Las pruebas unitarias deben ser rápidas. Probar su base de datos ralentiza sus pruebas de unidad significativamente a un nivel que no es aceptable. Las pruebas unitarias no deberían demorar más de 10 minutos, incluso cuando tenga miles de ellas.
David
@David ¿Tiene una base de datos disponible en su línea ci / cd? Claro, esa es una característica bastante estándar . Por cierto, no estoy abogando por las pruebas de integración en lugar de las pruebas unitarias , sino por las pruebas de integración junto con las pruebas unitarias. Las pruebas unitarias rápidas son esenciales, pero las bases de datos son demasiado complejas para confiar en pruebas unitarias con interacciones simuladas.
el.pescado
@ el.pescado Tengo que estar en desacuerdo. Si la comunicación de su base de datos está detrás de una capa de abstracción, es realmente fácil burlarse. Simplemente puede decidir qué objeto devolver. Además, el hecho de que algo sea estándar no lo convierte en algo bueno.
David
@David Creo que depende de cómo abordes la base de datos. ¿Es detalle de implementación o parte vital del sistema ? (Me inclino hacia lo último) . Si trata la base de datos como un almacenamiento tonto de datos, entonces sí, podría prescindir de las pruebas de integración. Sin embargo, si hay alguna lógica en la base de datos: restricciones, disparadores, claves externas, transacciones o su capa de datos usa SQL personalizado en lugar de métodos ORM simples, creo que las pruebas unitarias por sí solas no serán suficientes.
el.pescado
6

Necesitas ambos.

En su ejemplo, si estaba probando que una base de datos en una determinada condición, cuando findByKeywordse ejecuta el método, recupera los datos que espera que sean una prueba de integración excelente.

En cualquier otro código que esté usando ese findByKeywordmétodo, desea controlar lo que se está alimentando a la prueba, para que pueda devolver valores nulos o las palabras correctas para su prueba o lo que sea, luego se burla de la dependencia de la base de datos para que sepa exactamente lo que hará su prueba recibir (y pierde la sobrecarga de conectarse a una base de datos y asegurarse de que los datos que contiene son correctos)

Froome
fuente
6

El autor del artículo de blog al que se refiere está principalmente preocupado por la complejidad potencial que puede surgir de las pruebas integradas (aunque está escrito de una manera muy cauta y categórica). Sin embargo, las pruebas integradas no son necesariamente malas, y algunas en realidad son más útiles que las pruebas unitarias puras. Realmente depende del contexto de su aplicación y de lo que está tratando de probar.

Muchas aplicaciones de hoy simplemente no funcionarían si su servidor de base de datos dejara de funcionar. Al menos, piénselo en el contexto de la función que está intentando probar.

Por un lado, si lo que está tratando de probar no depende, o puede hacerse que no dependa en absoluto, de la base de datos, escriba su prueba de tal manera que ni siquiera intente usar el base de datos (solo proporcione datos simulados según sea necesario). Por ejemplo, si está tratando de probar alguna lógica de autenticación cuando sirve una página web (por ejemplo), probablemente sea bueno separar eso del DB por completo (suponiendo que no confíe en el DB para la autenticación, o eso puedes burlarte de manera razonablemente fácil).

Por otro lado, si es una característica que depende directamente de su base de datos y que no funcionaría en un entorno real si la base de datos no estuviera disponible, entonces se burla de lo que hace la base de datos en su código de cliente de base de datos (es decir, la capa que usa esa DB) no necesariamente tiene sentido.

Por ejemplo, si sabe que su aplicación va a depender de una base de datos (y posiblemente de un sistema de base de datos específico), burlarse del comportamiento de la base de datos en aras de la misma a menudo será una pérdida de tiempo. Los motores de base de datos (especialmente RDBMS) son sistemas complejos. Unas pocas líneas de SQL pueden realizar mucho trabajo, lo que sería difícil de simular (de hecho, si su consulta SQL es de unas pocas líneas, es probable que necesite muchas más líneas de Java / PHP / C # / Python código para producir el mismo resultado internamente): duplicar la lógica que ya ha implementado en la base de datos no tiene sentido, y verificar ese código de prueba se convertiría en un problema en sí mismo.

No necesariamente trataría esto como un problema de prueba unitaria versus prueba integrada , sino que miro el alcance de lo que se está probando. Los problemas generales de las pruebas de unidad e integración permanecen: necesita un conjunto razonablemente realista de datos de prueba y casos de prueba, pero algo que también sea lo suficientemente pequeño como para que las pruebas se ejecuten rápidamente.

El tiempo para restablecer la base de datos y repoblar con datos de prueba es un aspecto a considerar; generalmente evaluaría esto en función del tiempo que lleva escribir ese código simulado (que eventualmente tendría que mantener también).

Otro punto a considerar es el grado de dependencia que tiene su aplicación con la base de datos.

  • Si su aplicación simplemente sigue un modelo CRUD, donde tiene una capa de abstracción que le permite cambiar entre cualquier RDBMS por el simple medio de una configuración, es probable que pueda trabajar con un sistema simulado con bastante facilidad (posiblemente borroso la línea entre la unidad y las pruebas integradas utilizando un RDBMS en memoria).
  • Si su aplicación utiliza una lógica más compleja, algo que sería específico de SQL Server, MySQL, PostgreSQL (por ejemplo), entonces tendría más sentido tener una prueba que use ese sistema específico.
Bruno
fuente
"Muchas aplicaciones hoy simplemente no funcionarían si su servidor de base de datos se cayera", ¡ese es un punto realmente importante!
el.pescado
Buena explicación de los límites de los simulacros complejos, como usar otro lenguaje para simular SQL. Cuando el código de prueba se complica lo suficiente como para parecer que necesita ser probado, es un olor a control de calidad.
dodgethesteamroller
1

Tienes razón al pensar que tal prueba unitaria está incompleta. Lo incompleto está en la interfaz de la base de datos que se está burlando. La expectativa o afirmaciones ingenuas de este tipo son incompletas.

Para completarlo, tendría que dedicar suficiente tiempo y recursos para escribir o integrar un motor de reglas SQL que garantice que la instrucción SQL emitida por el sujeto bajo prueba, resulte en las operaciones esperadas.

Sin embargo, la alternativa / acompañante a menudo olvidada y algo costosa a la burla es la "virtualización" .

¿Puede activar una instancia de base de datos temporal, en memoria pero "real" para probar una sola función? si ? allí, tiene una mejor prueba, la que sí verifica los datos reales guardados y recuperados.

Ahora, uno podría decir, convirtió una prueba unitaria en una prueba de integración. Existen diferentes puntos de vista sobre dónde dibujar la línea para clasificar entre pruebas unitarias y pruebas de integración. En mi humilde opinión, "unidad" es una definición arbitraria y debe adaptarse a sus necesidades.

Dakota del Sur
fuente
1
esto parece simplemente repetir los puntos hechos y explicados en esta respuesta anterior que fue publicada hace varias horas
mosquito
0

Unit Testsy Integration Testsson ortogonales entre sí. Ofrecen una vista diferente de la aplicación que está creando. Usualmente quieres los dos . Pero el momento en el tiempo difiere, cuando quieres qué tipo de pruebas.

Lo que más a menudo quieras Unit Tests. Las pruebas unitarias se centran en una pequeña porción del código que se está probando; lo que se llama exactamente a unitse deja al lector. Pero el propósito es simple: obtener comentarios rápidos sobre cuándo y dónde se rompió su código . Dicho esto, debe quedar claro, que las llamadas a una base de datos real son un no .

Por otro lado, hay cosas que solo se pueden probar en condiciones difíciles sin una base de datos. Quizás haya una condición de carrera en su código y una llamada a un DB arroje una violación de una unique constraintque solo podría lanzarse si realmente usa su sistema. Pero ese tipo de pruebas son costosas que no puede (y no quiere) ejecutarlas con tanta frecuencia unit tests.

Thomas Junk
fuente
0

En el mundo .Net tengo la costumbre de crear un proyecto de prueba y crear pruebas como método de codificación / depuración / prueba de ida y vuelta menos la IU. Esta es una forma eficiente de desarrollarme. No estaba tan interesado en ejecutar todas las pruebas para cada compilación (porque ralentiza mi flujo de trabajo de desarrollo), pero entiendo la utilidad de esto para un equipo más grande. Sin embargo, puede establecer una regla que, antes de confirmar el código, todas las pruebas se ejecuten y aprueben (si las pruebas tardan más en ejecutarse porque la base de datos está siendo atacada).

Mockear la capa de acceso a datos (DAO) y no llegar a la base de datos, no solo no me permite codificar de la manera que me gusta y me he acostumbrado, sino que pierde una gran parte de la base de código real. Si realmente no está probando la capa de acceso a datos y la base de datos y solo está fingiendo, y luego pasa mucho tiempo burlándose de las cosas, no entiendo la utilidad de este enfoque para probar realmente mi código. Estoy probando una pieza pequeña en lugar de una más grande con una prueba. Entiendo que mi enfoque podría estar más en la línea de una prueba de integración, pero parece que la prueba unitaria con el simulacro es una pérdida de tiempo redundante si en realidad solo escribe la prueba de integración una vez y primero. También es una buena forma de desarrollar y depurar.

De hecho, desde hace un tiempo conozco TDD y Behavior Driven Design (BDD) y pienso en formas de usarlo, pero es difícil agregar pruebas unitarias retroactivamente. Quizás estoy equivocado, pero escribir una prueba que cubra más código de principio a fin con la base de datos incluida, parece una prueba mucho más completa y de mayor prioridad para escribir que cubre más código y es una forma más eficiente de escribir pruebas.

De hecho, creo que algo como Behavior Driven Design (BDD) que intenta probar de principio a fin con un lenguaje específico de dominio (DSL) debería ser el camino a seguir. Tenemos SpecFlow en el mundo .Net, pero comenzó como código abierto con Cucumber.

https://cucumber.io/

Realmente no estoy impresionado con la verdadera utilidad de la prueba que escribí burlándose de la capa de acceso a datos y sin llegar a la base de datos. El objeto devuelto no alcanzó la base de datos y no se rellenó con datos. Era un objeto completamente vacío que tuve que burlarme de una manera antinatural. Solo creo que es una pérdida de tiempo.

Según Stack Overflow, la burla se usa cuando los objetos reales no son prácticos para incorporar en la prueba de la unidad.

https://stackoverflow.com/questions/2665812/what-is-mocking

"La burla se usa principalmente en pruebas unitarias. Un objeto bajo prueba puede tener dependencias de otros objetos (complejos). Para aislar el comportamiento del objeto que desea probar, reemplace los otros objetos por simulacros que simulan el comportamiento de los objetos reales. Esto es útil si los objetos reales no son prácticos para incorporar en la prueba unitaria ".

Mi argumento es que si estoy codificando algo de extremo a extremo (interfaz de usuario web a capa empresarial a capa de acceso a datos a base de datos, ida y vuelta), antes de registrar algo como desarrollador, voy a probar este flujo de ida y vuelta. Si elimino la interfaz de usuario y depuro y pruebo este flujo a partir de una prueba, estoy probando todo menos la interfaz de usuario y devolviendo exactamente lo que la interfaz de usuario espera. Todo lo que me queda es enviar a la interfaz de usuario lo que quiere.

Tengo una prueba más completa que es parte de mi flujo de trabajo de desarrollo natural. Para mí, esa debería ser la prueba de mayor prioridad que cubra probar la especificación real del usuario de principio a fin tanto como sea posible. Si nunca creo otras pruebas más granulares, al menos tengo esta prueba más completa que demuestra que mi funcionalidad deseada funciona.

Un cofundador de Stack Exchange no está convencido de los beneficios de tener una cobertura de prueba de unidad del 100%. Yo tampoco lo soy. Tomaría una "prueba de integración" más completa que afecta a la base de datos en lugar de mantener un montón de simulaciones de bases de datos cualquier día.

https://www.joelonsoftware.com/2009/01/31/from-podcast-38/

usuario3198764
fuente
es tan obvio que no comprende la diferencia entre las pruebas unitarias y de integración
BЈовић
Creo que depende del proyecto. En proyectos más pequeños con menos recursos, donde un desarrollador es más responsable en general de las pruebas y las pruebas de regresión debido a la falta de probadores y de mantener la documentación sincronizada con el código, si voy a pasar algún tiempo escribiendo pruebas, serán las que me da más por mi dinero. Quiero matar tantos pájaros de un tiro como sea posible. Si la mayor parte de mi lógica y errores provienen de procedimientos almacenados en la base de datos que generan informes, o del JavaScript front-end, tener una cobertura de prueba de unidad completa en el nivel medio no ayuda mucho.
user3198764
-1

Las dependencias externas se deben burlar porque no puede controlarlas (pueden pasar durante la fase de prueba de integración pero fallar en la producción). Las unidades pueden fallar, las conexiones de la base de datos pueden fallar por varias razones, podría haber problemas de red, etc. Tener pruebas de integración no da ninguna confianza adicional porque son todos problemas que pueden ocurrir en tiempo de ejecución.

Con las pruebas unitarias verdaderas, está probando dentro de los límites de la caja de arena y debería estar claro. Si un desarrollador escribió una consulta SQL que falló en QA / PROD, significa que ni siquiera lo probaron una vez antes de ese momento.

vidalsasoon
fuente
+1 para "no puede controlarlos (pueden pasar durante la fase de prueba de integración pero fallar en la producción)" .
Tulains Córdova
Usted puede controlarlos a satisfactionary grado.
el.pescado
Entiendo tu punto, pero ¿creo que esto solía ser más cierto de lo que es hoy? Con la automatización y las herramientas (como Docker), puede replicar y repetir la configuración de todas sus dependencias binarias / de servidor de manera precisa y confiable para los paquetes de pruebas de integración. Por supuesto, sí, el hardware físico (y servicios de terceros, etc.) puede fallar.
mindplay.dk
55
Absolutamente no estoy de acuerdo. Debe escribir pruebas de integración (adicionales) porque las dependencias externas pueden fallar. Las dependencias externas pueden tener sus propias peculiaridades, que probablemente extrañarás cuando te burles de todo.
Paul Kertscher
1
@PaulK sigue pensando qué respuesta marcar como aceptada, pero me estoy inclinando hacia la misma conclusión.
mindplay.dk