¿Por qué usar JUnit para las pruebas?

131

Tal vez mi pregunta es novata, pero realmente no puedo entender las circunstancias bajo las cuales usaría ?

Ya sea que escriba aplicaciones simples o más grandes, las pruebo con las System.outdeclaraciones y me parece bastante fácil.

¿Por qué crear clases de prueba con JUnit, carpetas innecesarias en el proyecto si todavía tenemos que llamar a los mismos métodos, verificar lo que devuelven y luego tenemos una sobrecarga de anotar todo?

¿Por qué no escribir una clase y probarla de una vez con, System.outpero no crear clases de prueba?

PD. Nunca he trabajado en grandes proyectos, solo estoy aprendiendo.

Entonces, ¿cuál es el propósito?

Artem Moskalev
fuente
77
¿Sabe que cada vez que cambia algo en su programa, todo su trabajo anterior de examinar manualmente la salida se invalida y tiene que rehacerlos desde el principio?
Thorbjørn Ravn Andersen
No solo las "pruebas", sino también las "pruebas inteligentes" son muy importantes. Aquí hay un buen ejemplo: wp.me/prMeE-11
akcasoy

Respuestas:

139

Eso no es prueba, es "mirar manualmente la salida" (conocida en el negocio como LMAO). Más formalmente se conoce como "buscar manualmente resultados anormales" (LMFAO). (Vea la nota abajo)

Cada vez que cambie el código, debe ejecutar la aplicación y LMFAO para todos los códigos afectados por esos cambios. Incluso en proyectos pequeños, esto es problemático y propenso a errores.

Ahora escale hasta 50k, 250k, 1m LOC o más, y LMFAO cada vez que realice un cambio de código. No solo es desagradable, es imposible: ha ampliado las combinaciones de entradas, salidas, indicadores, condiciones y es difícil ejercer todas las ramas posibles.

Peor aún, LMFAO podría significar visitar páginas sobre páginas de aplicaciones web, ejecutar informes, revisar millones de líneas de registro en docenas de archivos y máquinas, leer correos electrónicos generados y entregados, verificar mensajes de texto, verificar la ruta de un robot, llenar una botella de refresco, agregando datos de un centenar de servicios web, comprobando el seguimiento de auditoría de una transacción financiera ... ya se entiende la idea. "Salida" no significa unas pocas líneas de texto, "salida" significa comportamiento agregado del sistema.

Por último, las pruebas de unidad y comportamiento definen el comportamiento del sistema. Las pruebas pueden ser ejecutadas por un servidor de integración continua y comprobar su corrección. Claro, también puedeSystem.out , pero el servidor de CI no va a saber si uno de ellos está equivocado, y si es así, son pruebas unitarias, y también podría usar un marco.

No importa cuán buenos pensemos que somos, los humanos no son buenos marcos de prueba de unidad o servidores CI.


Nota: LMAO está probando, pero en un sentido muy limitado. No es repetible de ninguna manera significativa en todo un proyecto o como parte de un proceso. Es similar a desarrollar de forma incremental en un REPL, pero nunca formalizar esas pruebas incrementales.

Dave Newton
fuente
3
-1 para la primera oración, que es completa y completamente falsa.
Michael Borgwardt
50

Escribimos pruebas para verificar la corrección del comportamiento de un programa.

La verificación de la corrección del comportamiento de un programa mediante la inspección del contenido de las declaraciones de salida utilizando sus ojos es un proceso manual , o más específicamente, visual .

Podrías argumentar que

la inspección visual funciona , verifico que el código hace lo que debe hacer, para estos escenarios y una vez que puedo ver que es correcto, estamos listos para comenzar.

Ahora, en primer lugar, es genial que estés interesado en saber si el código funciona correctamente o no. Eso es bueno. Estás por delante de la curva! Lamentablemente, hay problemas con esto como un enfoque.

El primer problema con la inspección visual es que eres un mal accidente de soldadura lejos de nunca poder verificar la corrección de tu código nuevamente.

El segundo problema es que el par de ojos utilizados está estrechamente acoplado con el cerebro del dueño de los ojos. Si el autor del código también posee los ojos utilizados en el proceso de inspección visual, el proceso de verificar la corrección depende del conocimiento sobre el programa internalizado en el cerebro del inspector visual.

Es difícil que un nuevo par de ojos entren y verifiquen la corrección del código simplemente porque no están asociados con el cerebro del codificador original. El propietario del segundo par de ojos tendrá que conversar con el autor original del código para comprender completamente el código en cuestión. La conversación como un medio para compartir conocimiento es notoriamente poco confiable. Un punto que es discutible si el codificador original no está disponible para el nuevo par de ojos. En ese caso, el nuevo par de ojos tiene que leer el código original.

Leer el código de otras personas que no está cubierto por las pruebas unitarias es más difícil que leer el código que tiene pruebas unitarias asociadas. En el mejor de los casos, leer el código de otras personas es un trabajo complicado, en el peor de los casos, esta es la tarea más difícil en la ingeniería de software. Hay una razón por la cual los empleadores, cuando anuncian ofertas de trabajo, enfatizan que un proyecto es nuevo (o completamente nuevo). Escribir código desde cero es más fácil que modificar el código existente y, por lo tanto, hace que el trabajo anunciado parezca más atractivo para los empleados potenciales.

Con las pruebas unitarias dividimos el código en sus componentes. Para cada componente, establecemos nuestro puesto indicando cómo debe comportarse el programa . Cada prueba unitaria cuenta una historia de cómo esa parte del programa debe actuar en un escenario específico. Cada prueba unitaria es como una cláusula en un contrato que describe lo que debería suceder desde el punto de vista del código del cliente.

Esto significa que un nuevo par de ojos tiene dos hilos de documentación en vivo y precisa sobre el código en cuestión.

Primero tienen el código en sí, la implementación, cómo se hizo el código ; segundo, tienen todo el conocimiento que el codificador original describió en un conjunto de declaraciones formales que cuentan la historia de cómo se supone que debe comportarse este código .

Las pruebas unitarias capturan y describen formalmente el conocimiento que poseía el autor original cuando implementaron la clase. Proporcionan una descripción de cómo se comporta esa clase cuando la usa un cliente.

Tiene razón al cuestionar la utilidad de hacer esto porque es posible escribir pruebas unitarias que son inútiles, no cubren todo el código en cuestión, se vuelven obsoletas o desactualizadas, etc. ¿Cómo nos aseguramos de que las pruebas unitarias no solo imiten, sino que mejoren el proceso de un autor concienzudo y concienzudo que inspeccione visualmente las declaraciones de salida de su código en tiempo de ejecución? Escriba la prueba de la unidad primero y luego escriba el código para hacer que la prueba pase. Cuando haya terminado, deje que las computadoras ejecuten las pruebas, son rápidas, son excelentes para realizar tareas repetitivas y son ideales para el trabajo.

Asegure la calidad de la prueba revisándolas cada vez que toque el código que prueban y ejecute las pruebas para cada compilación. Si una prueba falla, corríjala de inmediato.

Automatizamos el proceso de ejecución de pruebas para que se ejecuten cada vez que realizamos una compilación del proyecto. También automatizamos la generación de informes de cobertura de código que detalla qué porcentaje de código está cubierto y ejercido por las pruebas. Nos esforzamos por altos porcentajes. Algunas compañías evitarán que los cambios de código se registren en el control del código fuente si no tienen suficientes pruebas unitarias escritas para describir cualquier cambio en el comportamiento del código. Por lo general, un segundo par de ojos revisará los cambios de código junto con el autor de los cambios. El revisor realizará los cambios para garantizar que los cambios sean comprensibles y estén suficientemente cubiertos por las pruebas. Entonces el proceso de revisión es manual, pero cuando las pruebas (pruebas de unidad e integración y posiblemente pruebas de aceptación del usuario) pasan este proceso de revisión manual, pasan a formar parte del proceso de compilación automática. Se ejecutan cada vez que se registra un cambio. A El servidor lleva a cabo esta tarea como parte del proceso de compilación.

Las pruebas que se ejecutan automáticamente, mantienen la integridad del comportamiento del código y ayudan a evitar que futuros cambios en la base del código rompan el código .

Finalmente, proporcionar pruebas le permite volver a factorizar agresivamente el código porque puede hacer que las mejoras de código grandes sean seguras sabiendo que sus cambios no rompen las pruebas existentes.

Hay una advertencia para Test Driven Development y es que tienes que escribir código con el objetivo de hacerlo comprobable. Esto implica la codificación de interfaces y el uso de técnicas como la inyección de dependencias para crear instancias de objetos de colaboración. Echa un vistazo al trabajo de Kent Beck, que describe muy bien el TDD. Busque codificación de interfaces y estudie

Rob Kielty
fuente
13

Cuando prueba usando algo como System.out, solo prueba un pequeño subconjunto de posibles casos de uso. Esto no es muy exhaustivo cuando se trata de sistemas que podrían aceptar una cantidad casi infinita de entradas diferentes.

Las pruebas unitarias están diseñadas para permitirle ejecutar rápidamente pruebas en su aplicación utilizando un conjunto muy grande y diverso de entradas de datos diferentes. Además, las mejores pruebas unitarias también tienen en cuenta los casos límite, como las entradas de datos que se encuentran justo al borde de lo que se considera válido.

Para un ser humano, probar todas estas entradas diferentes podría llevar semanas, mientras que podría tomar minutos para una máquina.

Piénselo así: tampoco está "probando" algo que sea estático. Lo más probable es que su aplicación esté experimentando cambios constantes. Por lo tanto, estas pruebas unitarias están diseñadas para ejecutarse en diferentes puntos del ciclo de compilación o implementación. Quizás la mayor ventaja es esta:

Si rompe algo en su código, lo sabrá en este momento , no después de la implementación, no cuando un probador de control de calidad detecta un error, no cuando sus clientes lo han cancelado. También tendrá una mejor oportunidad de corregir el error de inmediato , ya que está claro que lo que rompió la parte del código en cuestión probablemente sucedió desde su última compilación. Por lo tanto, la cantidad de trabajo de investigación requerido para solucionar el problema se reduce considerablemente.

jmort253
fuente
9

Agregué otro System.out que NO puedo hacer:

  • Haga que cada caso de prueba sea independiente (es importante)

    JUnit puede hacerlo: cada vez que se creará una nueva instancia de caso de prueba y @Before se llama.

  • Código de prueba separado de la fuente

    JUnit puede hacerlo.

  • Integración con CI

    JUnit puede hacerlo con Ant y Maven.

  • Organice y combine casos de prueba fácilmente

    JUnit puede hacer @Ignorey probar suite.

  • Resultado fácil de verificar

    JUnit ofrece muchos métodos de Afirmación ( assertEquals, assertSame...)

  • El simulacro y el trozo te hacen concentrarte en el módulo de prueba.

    JUnit puede hacer: El uso de simulacro y código auxiliar le permite configurar el dispositivo correcto y centrarse en la lógica del módulo de prueba.

卢 声 远 Shengyuan Lu
fuente
9

Las pruebas unitarias aseguran que el código funcione según lo previsto. También son muy útiles para garantizar que el código siga funcionando según lo previsto en caso de que tenga que cambiarlo más adelante para crear nuevas funcionalidades para corregir un error. Tener una alta cobertura de prueba de su código le permite continuar desarrollando funciones sin tener que realizar muchas pruebas manuales.

Su enfoque manual System.outes bueno, pero no el mejor. Esta es una prueba única que realiza. En el mundo real, los requisitos siguen cambiando y la mayoría de las veces se realizan muchas modificaciones a las funciones y clases existentes. Entonces ... no cada vez que prueba el código ya escrito.

También hay algunas características más avanzadas en JUnit como

Afirmar declaraciones

JUnit proporciona métodos para probar ciertas condiciones, estos métodos generalmente comienzan con afirmaciones y le permiten especificar el mensaje de error, el resultado esperado y el resultado real

Algunos de estos métodos son

  1. fail([message])- Deja que la prueba falle. Puede usarse para verificar que no se llegue a cierta parte del código. O tener una prueba fallida antes de implementar el código de prueba.
  2. assertTrue(true)/ assertTrue(false)- Siempre será verdadero / falso. Se puede utilizar para predefinir un resultado de prueba, si la prueba aún no está implementada.
  3. assertTrue([message,] condition)- Comprueba que el booleano conditiones verdadero.
  4. assertEquals([message,] expected, actual)- Comprueba si dos valores son iguales (según el equalsmétodo si se implementa, de lo contrario, utilizando la ==comparación de referencia). Nota: Para las matrices, es la referencia la que está marcada, y no el contenido, use assertArrayEquals([message,] expected, actual)para eso.
  5. assertEquals([message,] expected, actual, delta)- Comprueba si dos valores flotantes o dobles están a cierta distancia uno del otro, controlados por el deltavalor.
  6. assertNull([message,] object) - Comprueba que el objeto es nulo

y así. Vea el Javadoc completo para todos los ejemplos aquí .

Suites

Con las suites de prueba, en cierto sentido, puede combinar varias clases de prueba en una sola unidad para poder ejecutarlas todas a la vez. Un ejemplo simple, combinando las clases de prueba MyClassTesty MySecondClassTesten una suite llamada AllTests:

import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;

@RunWith(Suite.class)
@SuiteClasses({ MyClassTest.class, MySecondClassTest.class })
public class AllTests { } 
Arvind
fuente
6

La principal ventaja de JUnit es que está automatizado en lugar de tener que consultarlo manualmente con sus impresiones. Cada prueba que escriba permanece con su sistema. Esto significa que si realiza un cambio que tiene un efecto secundario inesperado, su prueba lo detectará y fallará en lugar de tener que recordar probar manualmente todo después de cada cambio.

n00begon
fuente
4

JUnit es un marco de prueba de unidad para el lenguaje de programación Java. Es importante en el desarrollo impulsado por pruebas, y es uno de una familia de marcos de pruebas unitarias conocidos colectivamente como xUnit.

JUnit promueve la idea de "primera prueba y luego codificación", que hace hincapié en configurar los datos de prueba para un fragmento de código que puede probarse primero y luego implementarse. Este enfoque es como "prueba un poco, codifica un poco, prueba un poco, codifica un poco ..." que aumenta la productividad del programador y la estabilidad del código del programa que reduce el estrés del programador y el tiempo dedicado a la depuración.

Características JUnit es un marco de código abierto que se utiliza para escribir y ejecutar pruebas.

Proporciona anotaciones para identificar los métodos de prueba.

Proporciona aserciones para probar los resultados esperados.

Proporciona corredores de prueba para ejecutar pruebas.

Las pruebas JUnit le permiten escribir código más rápido, lo que aumenta la calidad

JUnit es elegantemente simple. Es menos complejo y lleva menos tiempo.

Las pruebas JUnit pueden ejecutarse automáticamente y verifican sus propios resultados y proporcionan comentarios inmediatos. No es necesario revisar manualmente un informe de resultados de la prueba.

Las pruebas JUnit se pueden organizar en conjuntos de pruebas que contienen casos de prueba e incluso otros conjuntos de pruebas.

Junit muestra el progreso de la prueba en una barra verde si la prueba va bien y se vuelve roja cuando falla una prueba.

AKKI
fuente
2

Tengo una perspectiva ligeramente diferente de por qué se necesita JUnit.

En realidad, puede escribir todos los casos de prueba usted mismo, pero es engorroso. Aquí están los problemas:

  1. En vez de System.out podemos agregar if(value1.equals(value2))y devolver 0 o -1 o mensaje de error. En este caso, necesitamos una clase de prueba "principal" que ejecute todos estos métodos y verifique los resultados y mantenga qué casos de prueba fallaron y cuáles se pasaron.

  2. Si desea agregar más pruebas, también debe agregarlas a esta clase de prueba "principal". Cambios al código existente. Si desea detectar automáticamente los casos de prueba de las clases de prueba, debe usar la reflexión.

  3. Eclipse no detecta todas sus pruebas y su clase principal para ejecutar pruebas y necesita escribir configuraciones personalizadas de depuración / ejecución para ejecutar estas pruebas. Sin embargo, todavía no ves esas bonitas salidas de color verde / rojo.

Esto es lo que está haciendo JUnit:

  1. Tiene assertXXX() métodos que son útiles para imprimir mensajes de error útiles de las condiciones y comunicar los resultados a la clase "principal".

  2. La clase "main" se llama runner que es proporcionada por JUnit, por lo que no tenemos que escribir ninguna. Y detecta los métodos de prueba automáticamente por reflexión. Si agrega nuevas pruebas con @Testanotaciones, se detectarán automáticamente.

  3. JUnit también tiene integración eclipse e integración maven / gradle, por lo que es fácil ejecutar pruebas y no tendrá que escribir configuraciones de ejecución personalizadas.

No soy un experto en JUnit, así que eso es lo que entendí a partir de ahora, agregaré más en el futuro.

Sreekar
fuente
Supongo que en la primera parte usted escribió lo que habríamos hecho si JUnit no estuviera allí para hacer que la unidad probara un poco mejor que las declaraciones system.out.println. Puede ser JUnit es el resultado de tales intentos de algunos programadores y sintieron la necesidad de escribir un marco de prueba separado para llevar a cabo esta automatización, por lo que nació JUnit.
Saurabh Patil
1

No puede escribir ningún caso de prueba sin usar el marco de prueba o de lo contrario tendrá que escribir su marco de prueba para hacer justicia a sus casos de prueba. Aquí hay información sobre JUnit Framework, aparte de que puede usar TestNG framework.

¿Qué es junit?

Junit es un marco de prueba ampliamente utilizado junto con Java Programming Language. Puede usar este marco de automatización tanto para pruebas unitarias como para pruebas de interfaz de usuario. Nos ayuda a definir el flujo de ejecución de nuestro código con diferentes anotaciones. Junit se basa en la idea de "primera prueba y luego codificación" que nos ayuda a aumentar la productividad de los casos de prueba y la estabilidad del código.

Características importantes de las pruebas Junit:

  1. Es un marco de prueba de código abierto que permite a los usuarios escribir y ejecutar casos de prueba de manera efectiva.
  2. Proporciona varios tipos de anotaciones para identificar métodos de prueba.
  3. Proporciona diferentes tipos de afirmaciones para verificar los resultados de la ejecución de casos de prueba.
  4. También proporciona corredores de prueba para ejecutar pruebas de manera efectiva.
  5. Es muy simple y, por lo tanto, ahorra tiempo.
  6. Proporciona formas de organizar sus casos de prueba en forma de trajes de prueba.
  7. Da resultados de casos de prueba de manera simple y elegante.
  8. Puede integrar jUnit con Eclipse, Android Studio, Maven & Ant, Gradle y Jenkins
anuja jain
fuente
0

JUNIT es el método que generalmente acepta el desarrollador de Java. Donde pueden proporcionar una entrada esperada similar a la función y decidir en consecuencia que el código escrito está perfectamente escrito o si el caso de prueba falla, entonces también puede ser necesario implementar un enfoque diferente. JUNIT acelerará el desarrollo y garantizará los 0 defectos en la función.

mohit sarsar
fuente
0

JUNIT: OBSERVAR Y AJUSTAR

Aquí está mi perspectiva de JUNIT.

JUNIT se puede utilizar para:
1) Observar el comportamiento del sistema cuando se agrega una nueva unidad en ese sistema.
2) Realice ajustes en el sistema para recibir la "nueva" unidad del sistema.
¿Qué? Exactamente.

La vida real, por ejemplo.

Cuando su pariente visite la habitación de su albergue universitario,
1) Pretenderá ser más responsable.
2) Mantendrá todas las cosas donde deberían estar, como zapatos en el estante de zapatos no en la silla, ropa en el armario no en la silla.
3) Te desharás de todo el contrabando.
4) comenzará la limpieza en cada dispositivo que posea.

En términos de programación

Sistema: su código
UNIDAD: nueva funcionalidad.
Como el marco JUNIT se utiliza para el lenguaje JAVA, JUNIT = UNIDAD JAVA (puede ser).

Suponga que ya tiene un código a prueba de balas, pero llegó un nuevo requisito y debe agregar el nuevo requisito en su código. Este nuevo requisito puede romper su código para alguna entrada (caso de prueba).

Una manera fácil de adaptar este cambio es mediante pruebas unitarias (JUNIT).
Para eso, debe escribir múltiples casos de prueba para su código cuando construya su base de código. Y cada vez que surge un nuevo requisito, simplemente ejecuta todos los casos de prueba para ver si algún caso de prueba falla. Si no, entonces eres un artista BadA ** y estás listo para implementar el nuevo código.
Si alguno de los casos de prueba falla, entonces cambia su código y vuelve a ejecutar los casos de prueba hasta que obtenga el estado verde.

Rohit Singh
fuente