¿Cuál es la diferencia entre integración y pruebas unitarias?

Conozco la definición de libro de texto de pruebas unitarias y pruebas de integración. Lo que me llama la atención es cuando llega el momento de escribir pruebas unitarias … Las escribiré para cubrir el mayor número posible de clases de clases.

Por ejemplo, si tengo una clase de Word , escribiré algunas pruebas de unidad para la clase de Word . Luego, empiezo a escribir mi clase de Sentence , y cuando necesite interactuar con la clase de Word , a menudo escribiré mis pruebas unitarias para que prueben tanto la Sentence como la Word … al menos en los lugares donde interactúan.

¿Estas pruebas se han convertido esencialmente en pruebas de integración porque ahora prueban la integración de estas 2 clases, o es solo una prueba unitaria que abarca 2 clases?

En general, debido a esta línea incierta, raramente escribiré pruebas de integración … o usaré el producto final para ver si todas las piezas funcionan correctamente, las pruebas de integración reales, aunque sean manuales y rara vez se repitan más allá del scope. de cada característica individual?

¿Estoy entendiendo mal las pruebas de integración, o realmente hay muy poca diferencia entre la integración y las pruebas unitarias?


Editar

¡Gracias por todas las excelentes respuestas a todos! Creo que por las variadas respuestas está claro que la línea entre la unidad y las pruebas de integración es definitivamente una línea borrosa, y tal vez es un poco pedante intentar descubrir cuáles son y cuál es el verdadero enfoque en el código (Gracias @Rob Cooper ) Además, lo siento pero no voy a aceptar ninguna respuesta porque demasiados son demasiado buenos, y realmente parece bastante subjetivo.

La diferencia clave, para mí, es que las pruebas de integración revelan si una característica está funcionando o está rota, ya que hacen hincapié en el código en un escenario cercano a la realidad. Invocan uno o más métodos o funciones de software y prueban si actúan como se espera.

Por el contrario, una prueba unitaria que prueba un solo método se basa en la suposición (a menudo errónea) de que el rest del software funciona correctamente, porque se burla explícitamente de cada dependencia.

Por lo tanto, cuando una prueba unitaria para un método que implementa alguna característica es verde, no significa que la característica está funcionando.

Digamos que tienes un método en algún lugar como este:

 public SomeResults DoSomething(someInput) { var someResult = [Do your job with someInput]; Log.TrackTheFactYouDidYourJob(); return someResults; } 

DoSomething es muy importante para su cliente: es una característica, lo único que importa. Es por eso que generalmente se escribe una especificación de Pepino que lo afirma: desea verificar y comunicar que la característica funciona o no.

 Feature: To be able to do something In order to do something As someone I want the system to do this thing Scenario: A sample one Given this situation When I do something Then what I get is what I was expecting for 

Sin dudas: si la prueba pasa, puede afirmar que está entregando una función de trabajo. Esto es lo que puede llamar Business Value .

Si desea escribir una prueba unitaria para DoSomething , debe pretender (utilizando algunos simulacros) que el rest de las clases y métodos funcionan (es decir, que todas las dependencias que utiliza el método funcionan correctamente) y afirmar que su método funciona .

En la práctica, haces algo como:

 public SomeResults DoSomething(someInput) { var someResult = [Do your job with someInput]; FakeAlwaysWorkingLog.TrackTheFactYouDidYourJob(); // Using a mock Log return someResults; } 

Puede hacer esto con Dependency Injection, o con algún Método de fábrica o cualquier Framework de simulacro o simplemente ampliando la clase bajo prueba.

Supongamos que hay un error en Log.DoSomething() . Afortunadamente, la especificación de Gherkin lo encontrará y sus pruebas de extremo a extremo fallarán.

La función no funcionará porque Log está roto, no porque [Do your job with someInput] no esté haciendo su trabajo. Y, por cierto, [Do your job with someInput] es la única responsabilidad de ese método.

Además, supongamos que Log se usa en otras 100 funciones, en 100 otros métodos de otras 100 clases.

Sí, 100 características fallarán. Pero, afortunadamente, 100 pruebas de extremo a extremo también están fallando y revelando el problema. Y, sí: están diciendo la verdad .

Es información muy útil: sé que tengo un producto roto. También es información muy confusa: no me dice nada sobre dónde está el problema. Me comunica el síntoma, no la causa raíz.

Sin embargo, la prueba de unidad de DoSomething es verde, porque está usando un Log falso, construido para nunca romperse. Y, sí: está mintiendo claramente . Se está comunicando que una característica rota funciona. ¿Cómo puede ser útil?

(Si la prueba de unidad de DoSomething() falla, asegúrese de que: [Do your job with someInput] tenga algunos errores).

Supongamos que este es un sistema con una clase rota: Un sistema con una clase rota

Un solo error romperá varias características y fallarán varias pruebas de integración.

Un único error romperá varias características y fallarán varias pruebas de integración

Por otro lado, el mismo error romperá solo una prueba unitaria.

El mismo error romperá solo una unidad de prueba

Ahora, compara los dos escenarios.

El mismo error romperá solo una prueba unitaria.

  • Todas las funciones que utilizan el Log roto son rojas
  • Todas las pruebas de su unidad son verdes, solo la prueba unitaria para Log es roja

En realidad, las pruebas unitarias para todos los módulos que usan una característica rota son verdes porque, al usar simulaciones, eliminaron las dependencias. En otras palabras, se ejecutan en un mundo ideal, completamente ficticio. Y esta es la única forma de aislar errores y buscarlos. Pruebas unitarias significa burlarse. Si no te estás burlando, no eres unitario.

La diferencia

Las pruebas de integración dicen lo que no funciona. Pero no sirven para adivinar dónde podría estar el problema.

Las pruebas unitarias son las únicas pruebas que le dicen dónde exactamente está el error. Para dibujar esta información, deben ejecutar el método en un entorno burlado, donde se supone que todas las otras dependencias funcionan correctamente.

Es por eso que creo que su oración “O es solo una prueba unitaria que abarca 2 clases” de alguna manera está desplazada. Una prueba unitaria nunca debe abarcar 2 clases.

Esta respuesta es básicamente un resumen de lo que escribí aquí: las pruebas de unidad mienten, es por eso que los amo .

Cuando escribo pruebas unitarias, limito el scope del código que se está probando a la clase que estoy escribiendo actualmente mediante dependencias de burla. Si estoy escribiendo una clase de oraciones, y la oración tiene una dependencia en Word, usaré una palabra falsa. Al burlar Word, puedo centrarme solo en su interfaz y probar los diversos comportamientos de mi clase Sentence a medida que interactúa con la interfaz de Word. De esta forma solo estoy probando el comportamiento y la implementación de Sentence y no al mismo tiempo probando la implementación de Word.

Una vez que he escrito las pruebas unitarias para asegurarme de que Sentence se comporta correctamente cuando interactúa con Word en base a la interfaz de Word, entonces escribo la prueba de integración para asegurarme de que mis suposiciones sobre las interacciones fueron correctas. Para esto, proporciono los objetos reales y escribo una prueba que ejerce una característica que terminará usando tanto Sentence como Word.

Mis 10 bits: D

Siempre me dijeron que Unit Tests es la prueba de un componente individual , que debe ejercerse al máximo. Ahora, esto tiende a tener muchos niveles, ya que la mayoría de los componentes están hechos de partes más pequeñas. Para mí, una unidad es una parte funcional del sistema. Por lo tanto, debe proporcionar algo de valor (es decir, no un método para analizar cadenas, sino un HtmlSanitizer tal vez).

Las pruebas de integración son el siguiente paso, es tomar uno o más componentes y asegurarse de que funcionen como deberían. Usted está por encima de las complejidades de la preocupación sobre cómo funcionan los componentes individualmente, pero cuando ingresa html en su HtmlEditControl , de alguna manera mágicamente sabe si es válido o no ..

Sin embargo, es una línea movible real. Prefiero centrarme más en hacer que el maldito código funcione completamente. ^ _ ^

Pruebas unitarias usan burlas

De lo que estás hablando son pruebas de integración que realmente prueban la integración completa de tu sistema. Pero cuando realizas pruebas unitarias debes probar cada unidad por separado. Todo lo demás debería ser burlado. Entonces, en su caso de clase Sentence , si usa la clase Word , entonces su clase de Word debería ser burlada. De esta forma, solo Sentence la funcionalidad de la clase Sentence .

Creo que cuando comienzas a pensar en pruebas de integración, estás hablando más de un cruce entre capas físicas que capas lógicas.

Por ejemplo, si sus pruebas se refieren a la generación de contenido, es una prueba unitaria: si su prueba se refiere únicamente a la escritura en el disco, sigue siendo una prueba unitaria, pero una vez que prueba ambas E / S Y el contenido del archivo, entonces tienes una prueba de integración. Cuando prueba la salida de una función dentro de un servicio, es una prueba de unidad, pero una vez que realiza una llamada de servicio y ve si el resultado de la función es el mismo, entonces es una prueba de integración.

Técnicamente, no puedes probar la unidad de una sola clase de todos modos. ¿Qué pasa si tu clase está compuesta con varias otras clases? ¿Eso lo convierte automáticamente en una prueba de integración? No lo creo.

Pruebas unitarias: las pruebas realizadas a una unidad o a una pieza de software más pequeña. Hecho para verificar si satisface su especificación funcional o su estructura de diseño prevista.

Pruebas de integración: prueba de los módulos relacionados en conjunto por su funcionalidad combinada.

usando el diseño de responsabilidad única, es en blanco y negro. Más de 1 responsabilidad, es una prueba de integración.

Por la prueba de pato (mira, charlatanes, patos, es un pato), es solo una prueba de unidad con más de 1 objeto nuevo en ella.

Cuando entra en mvc y lo prueba, las pruebas del controlador siempre son integradas, porque el controlador contiene una unidad modelo y una unidad de visualización. Probando la lógica en ese modelo, yo llamaría una prueba unitaria.

En mi opinión, la respuesta es “¿Por qué importa?”

¿Es porque las pruebas unitarias son algo que usted hace y las pruebas de integración son algo que usted no hace? ¿O viceversa? Por supuesto que no, deberías tratar de hacer ambas cosas.

¿Se debe a que las pruebas unitarias deben ser rápidas, aisladas, repetibles, autovalidantes y oportunas, y las pruebas de integración no deberían ser así? Por supuesto que no, todas las pruebas deberían ser estas.

¿Es porque usa burlas en pruebas unitarias pero no las usa en pruebas de integración? Por supuesto no. Esto implicaría que, si tengo una prueba de integración útil, no tengo permitido agregar un simulacro para una parte, me temo que tendré que cambiar el nombre de mi prueba a “prueba de unidad” o entregársela a otro progtwigdor para que trabaje.

¿Es porque las pruebas unitarias prueban una unidad y las pruebas de integración prueban varias unidades? Por supuesto no. ¿De qué importancia práctica es eso? La discusión teórica sobre el scope de las pruebas se rompe en la práctica de todos modos porque el término “unidad” depende completamente del contexto. En el nivel de clase, una unidad podría ser un método. En un nivel de ensamblaje, una unidad podría ser una clase, y en el nivel de servicio, una unidad podría ser un componente. E incluso las clases usan otras clases, ¿cuál es la unidad?

No tiene importancia.

Las pruebas son importantes, PRIMERO es importante, dividir los pelos sobre las definiciones es una pérdida de tiempo que solo confunde a los recién llegados con las pruebas.

Piensa en la naturaleza de tus pruebas en los siguientes términos:

  • Reducción de riesgos : para eso están los exámenes. Solo una combinación de pruebas unitarias y pruebas de integración puede brindarle una reducción total del riesgo, ya que las pruebas unitarias no pueden probar la interacción adecuada entre los módulos y las pruebas de integración solo pueden ejercitar la funcionalidad de un módulo no trivial en menor grado.
  • Esfuerzo de escritura de prueba : las pruebas de integración pueden ahorrar esfuerzo porque no necesita escribir stubs / fakes / mocks. Pero las pruebas unitarias también pueden ahorrar esfuerzo, cuando implementar (y mantener) esos trozos / falsificaciones / burlas es más fácil que configurar la configuración de prueba sin ellos.
  • Retraso en la ejecución de la prueba : las pruebas de integración que involucran operaciones pesadas (como el acceso a sistemas externos como bases de datos o servidores remotos) tienden a ser lentas. Esto significa que las pruebas unitarias pueden ejecutarse con mucha más frecuencia, lo que reduce el esfuerzo de depuración si algo falla, porque usted tiene una mejor idea de lo que ha cambiado mientras tanto. Esto se vuelve particularmente importante si usa desarrollo impulsado por prueba (TDD).
  • Esfuerzo de depuración : si una prueba de integración falla, pero ninguna de las pruebas de la unidad lo hace, esto puede ser muy inconveniente, porque hay tanto código involucrado que puede contener el problema. Este no es un gran problema si anteriormente ha cambiado solo unas pocas líneas, pero como las pruebas de integración se ejecutan lentamente, quizás no las haya ejecutado en intervalos tan cortos …

Recuerde que una prueba de integración aún puede resguardar / falsificar / burlarse de algunas de sus dependencias. Esto proporciona una gran cantidad de terreno intermedio entre las pruebas unitarias y las pruebas del sistema (las pruebas de integración más completas, probando todo el sistema).

Por lo tanto, un enfoque pragmático sería: confiar de manera flexible en las pruebas de integración tanto como sea posible y usar pruebas unitarias donde esto sería demasiado arriesgado o inconveniente. Esta forma de pensar puede ser más útil que alguna discriminación dogmática de pruebas unitarias y pruebas de integración.

Creo que aún llamaría a un par de clases interactivas una prueba unitaria siempre que las pruebas de la unidad para clase 1 estén probando las características de la clase 1, y las pruebas unitarias para la clase 2 estén probando sus características, y también que no estén llegando a la base de datos.

Llamo a una prueba una prueba de integración cuando se ejecuta en la mayor parte de mi stack e incluso llega a la base de datos.

Me gusta mucho esta pregunta, porque la discusión de TDD a veces me parece demasiado purista, y es bueno para mí ver algunos ejemplos concretos.

Yo hago lo mismo, los llamo a todos como pruebas unitarias, pero en algún momento tengo una “prueba de unidad” que cubre tanto que a menudo cambio el nombre a “Prueba de integración”: solo un cambio de nombre, nada más cambia.

Creo que hay una continuación de las “pruebas atómicas” (probar una clase pequeña o un método) para pruebas unitarias (nivel de clase) y pruebas de integración, y luego prueba funcional (que normalmente cubren muchas más cosas de arriba hacia abajo) – no parece haber un corte limpio.

Si tu prueba configura datos, y tal vez carga una base de datos / archivo, etc. quizás sea más una prueba de integración (pruebas de integración que encuentro utilizan menos simulacros y más clases reales, pero eso no significa que no puedas burlarte de algunos del sistema).

Unit Testing es un método de prueba que verifica que las unidades individuales del código fuente funcionen correctamente.

La prueba de integración es la fase de prueba de software en la que los módulos de software individuales se combinan y prueban como un grupo.

Wikipedia define una unidad como la parte más pequeña comprobable de una aplicación, que en Java / C # es un método. Pero en su ejemplo de clase de Word y Sentencia, probablemente solo escribiría las pruebas para la oración, ya que probablemente me resultaría excesivo usar una clase de palabras simuladas para probar la clase de oraciones. Entonces la oración sería mi unidad y la palabra es un detalle de implementación de esa unidad.

Pruebas de integración: se prueba la persistencia de la base de datos.
Pruebas unitarias: se burla del acceso a la base de datos. Los métodos de código son probados.

Las pruebas unitarias están probando contra una unidad de trabajo o un bloque de código si lo desea. Por lo general, realizado por un solo desarrollador.

Las pruebas de integración se refieren a la prueba que se realiza, preferiblemente en un servidor de integración, cuando un desarrollador confirma su código en un repository de control de origen. Las pruebas de integración pueden ser realizadas por servicios públicos como Cruise Control.

Entonces, usted realiza las pruebas de su unidad para validar que la unidad de trabajo que ha construido está funcionando y luego la prueba de integración valida que todo lo que haya agregado al repository no rompió ninguna otra cosa.

Un poco académica esta pregunta, ¿no? 😉 Mi punto de vista: Para mí, una prueba de integración es la prueba de la parte completa, no si dos de cada diez partes van juntas. Nuestra prueba de integración muestra si la comstackción maestra (que contiene 40 proyectos) tendrá éxito. Para los proyectos tenemos toneladas de pruebas unitarias. Lo más importante para mí es que una prueba unitaria no dependa de otra prueba unitaria. Entonces, para mí, ambas pruebas que describes más arriba son pruebas unitarias, si son independientes. Para pruebas de integración, esto no necesita ser importante.

Llamo pruebas unitarias a esas pruebas de que la caja blanca prueba una clase. Cualquier dependencia que la clase requiera se reemplaza por falsas (simulacros).

Las pruebas de integración son aquellas en las que se prueban múltiples clases y sus interacciones al mismo tiempo. Solo algunas dependencias en estos casos son falsificadas / burladas.

No llamaría a las pruebas de integración del Controlador a menos que una de sus dependencias sea real (es decir, no sea falsa) (por ejemplo, IFormsAuthentication).

Separar los dos tipos de pruebas es útil para probar el sistema en diferentes niveles. Además, las pruebas de integración tienden a durar mucho tiempo y se supone que las pruebas unitarias son rápidas. La distinción de velocidad de ejecución significa que se ejecutan de manera diferente. En nuestros procesos de desarrollo, las pruebas unitarias se ejecutan al momento del check-in (lo cual está bien porque son muy rápidas), y las pruebas de integración se ejecutan una o dos veces por día. Intento y ejecuto pruebas de integración tan a menudo como sea posible, pero generalmente presiono la base de datos / escritura en archivos / haciendo que rpc’s / etc se desacelere.

Eso plantea otro punto importante, las pruebas unitarias deben evitar golpear IO (por ejemplo, disco, red, db). De lo contrario, reducen la velocidad mucho. Se necesita un poco de esfuerzo para diseñar estas dependencias de IO. No puedo admitir que he sido fiel a la regla de “pruebas unitarias deben ser rápidas”, pero si lo es, los beneficios en un sistema mucho más grande se hacen evidentes muy rápidamente. .

Los ejemplos anteriores funcionan muy bien y no necesito repetirlos. Así que me concentraré en usar ejemplos para ayudarlo a comprender.

Pruebas de integración

Las pruebas de integración comprueban si todo está funcionando en conjunto. Imagine una serie de engranajes trabajando juntos en un reloj. Una prueba de integración sería: ¿está el reloj diciendo la hora correcta? ¿Sigue diciendo la hora correcta en 3 días?

Todo lo que te dice es si la pieza en general está funcionando. Si falla: no te dice exactamente dónde está fallando.

Pruebas unitarias

Estos son tipos de prueba realmente específicos. Le dicen si una cosa específica está funcionando o está fallando. La clave para este tipo de prueba es que prueba solo una cosa específica mientras se supone que todo lo demás está funcionando bien. Ese es el punto clave.

Ejemplo: Desarrollemos en este punto usando un ejemplo:

  • Tomemos un automóvil como ejemplo.
  • Prueba de integración para un automóvil: por ejemplo, el automóvil conduce a Echuca y viceversa. Si lo hace, puede decir con seguridad que un automóvil está funcionando desde un punto de vista general. Es una prueba de integración. Si falla, no tiene idea de dónde está fallando: ¿es el radiador, la transmisión, el motor o el carburador? No tienes idea. Podría ser cualquier cosa.
  • Prueba de unidad para un automóvil: Que el motor está funcionando. Esta prueba supone que todo lo demás en el automóvil está funcionando bien. De esta forma, si falla esta prueba particular de la unidad: puede estar seguro de que el problema radica en el motor, por lo que puede aislar rápidamente y solucionar el problema.

Las consecuencias de mezclar tu integración y pruebas unitarias:

  • Supongamos que falla la prueba de integración de su automóvil. No conduce con éxito a Echuca. ¿Dónde está el problema?

  • Usted mira en su prueba de motor. Pero en este caso particular, la prueba del motor usa dependencias externas: usa un sistema de combustible especial. Supongamos que la prueba de la unidad del motor también ha fallado. En otras palabras, tanto la prueba de integración como la prueba de la unidad del motor han fallado. ¿Dónde está el problema? (Espere 10 segundos para obtener la respuesta.) • ¿Falló el motor o fue el sistema de combustible el que causó el fallo del motor?

¿Ves el problema aquí? No sabes exactamente qué está fallando. Si usa 10 dependencias, entonces cada una de esas 10 podría haber causado el problema, y ​​no sabrá por dónde empezar. Es por eso que las pruebas unitarias se burlan de asumir que todo lo demás está funcionando bien.

Espero que esto ayude.

¿Estas pruebas se han convertido esencialmente en pruebas de integración porque ahora prueban la integración de estas 2 clases? ¿O es solo una prueba unitaria que abarca 2 clases?

Creo que sí y sí Su prueba de unidad que abarca 2 clases se convirtió en una prueba de integración.

Podrías evitarlo probando la clase Sentence con la implementación simulada – clase MockWord, que es importante cuando esas partes del sistema son lo suficientemente grandes como para ser implementadas por diferentes desarrolladores. En ese caso, Word se prueba en una sola unidad, Sentence se prueba en unidades con la ayuda de MockWord, y luego Sentence se prueba en integración con Word.

Exaple de diferencia real puede estar siguiendo 1) Una matriz de 1,000,000 de elementos se prueba fácilmente en una unidad y funciona bien. 2) BubbleSort se prueba fácilmente en una matriz simulada de 10 elementos y también funciona bien 3) Las pruebas de integración muestran que algo no está tan bien.

Si estas partes son desarrolladas por una sola persona, lo más probable es que se encuentre un problema mientras la unidad prueba BubbleSoft solo porque el desarrollador ya tiene una matriz real y no necesita una implementación simulada.

Además, es importante recordar que tanto las pruebas unitarias como las pruebas de integración se pueden automatizar y escribir utilizando, por ejemplo, JUnit. En las pruebas de integración de JUnit, se puede usar la clase org.junit.Assume para probar la disponibilidad de los elementos del entorno (por ejemplo, conexión a la base de datos) u otras condiciones.

    Intereting Posts