¿Por qué las pruebas automatizadas siguen fallando en mi empresa?


178

Hemos intentado introducir pruebas automatizadas para desarrolladores varias veces en mi empresa. Nuestro equipo de control de calidad utiliza Selenium para automatizar las pruebas de IU, pero siempre quise presentar pruebas unitarias y pruebas de integración. En el pasado, cada vez que lo probábamos, todos se emocionaban durante el primer mes o dos. Luego, varios meses después, la gente simplemente deja de hacerlo.

Algunas observaciones y preguntas:

  1. ¿Las pruebas automatizadas realmente funcionan? La mayoría de mis colegas que solían trabajar en otras compañías han intentado y no han podido implementar una estrategia de prueba automatizada. Todavía no he visto una compañía de software de la vida real que realmente lo use y no solo hable de ello. Muchos desarrolladores ven las pruebas automatizadas como algo que en teoría es excelente, pero que en realidad no funciona. A nuestro equipo de negocios le encantaría que los desarrolladores lo hicieran incluso a un costo de 30% de tiempo extra (al menos así lo dicen). Pero los desarrolladores son escépticos.

  2. Nadie sabe realmente cómo realizar pruebas automatizadas correctamente. Sí, todos hemos leído los ejemplos de prueba de la unidad en Internet, pero usarlos para un gran proyecto es algo completamente diferente. El principal culpable es burlarse / tropezar la base de datos o cualquier otra cosa que no sea trivial. Terminas pasando más tiempo burlándose que escribiendo pruebas reales. Luego, cuando comienza a tomar más tiempo para escribir pruebas que el código, es cuando te rindes.

  3. ¿Existen buenos ejemplos de pruebas unitarias / pruebas de integración de sistemas utilizadas en aplicaciones web complejas centradas en datos? ¿Algún proyecto de código abierto? Nuestra aplicación está centrada en datos pero también tiene mucha lógica de dominio. Probé el enfoque del repositorio en algún momento y lo encontré bastante bueno para las pruebas unitarias, pero tuvo el precio de poder optimizar el acceso a los datos fácilmente y agregó otra capa de complejidad.

Tenemos un gran proyecto realizado por 20 desarrolladores experimentados. Este parece ser un entorno ideal para introducir pruebas unitarias / pruebas de integración.

¿Por qué no funciona para nosotros? ¿Cómo lo hiciste funcionar en tu empresa?


14
¿Cuál es tu pila de tecnología?
Florian Margaine

77
Los WebForms son casi imposibles de probar correctamente en la unidad. Puede usar un patrón MVP (Modelo / Vista / Presentador) para mover la lógica de presentación a un componente comprobable.
Pete

12
@MasonWheeler: En ambos casos, ha construido un argumento excelente que refuta las premisas que no fueron aceptadas en primer lugar: es decir, que las pruebas unitarias existen para demostrar la corrección.
Steven Evers

10
@ MasonWheeler: utilizando ese argumento, no debe intentar ningún control de calidad nunca, porque nunca probará que no hay errores. Ese ni siquiera es el objetivo. Una buena estrategia automatizada de UI y pruebas unitarias es simplemente liberar el control de calidad de las pruebas de memoria y permitirles concentrarse en las pruebas exploratorias.
Alex

15
Me sorprende que varias personas declararon que nunca habían visto pruebas automatizadas durante más de unos pocos meses. Trabajé para unas cinco grandes empresas en Alemania como consultor y te despedirían si no escribes pruebas. La prueba automatizada no es un tema teórico, se practica con éxito en todo el mundo y aumenta significativamente la calidad del código (si se hace correctamente).

Respuestas:


89

La parte más difícil de hacer pruebas unitarias es obtener la disciplina para escribir las pruebas primero / temprano. La mayoría de los desarrolladores están acostumbrados a sumergirse en el código. También ralentiza el proceso de desarrollo desde el principio, ya que está tratando de descubrir cómo escribir una prueba para el código. Sin embargo, a medida que mejora en las pruebas, esto se acelera. Y debido a las pruebas de escritura, la calidad inicial del código comienza más alta.

Al comenzar, intente escribir solo pruebas. No te preocupes tanto por burlarte de cosas al principio. Mantenga las pruebas simples. Las pruebas son de código y pueden / deben ser refactorizadas. Aunque en ese sentido, si algo es difícil de probar, también podría ser el diseño. TDD conduce hacia el uso de la mayoría de los patrones de diseño (en mi experiencia, particularmente el patrón Factory).

Asegúrese de que las pruebas obtengan un nivel de visibilidad. Integrelos en el proceso de lanzamiento, durante la revisión del código pregunte por ellos. Cualquier error encontrado debe hacerse una prueba. Estas cosas son donde brilla TDD.

Aquí hay un par de recursos que he encontrado útiles:

http://misko.hevery.com/attachments/Guide-Writing%20Testable%20Code.pdf

http://www.agitar.com/downloads/TheWayOfTestivus.pdf

Editar:

Una cosa a tener en cuenta al escribir exámenes. No está intentando especificar nada sobre la implementación del código, solo el comportamiento. Cuando escribes código, lo pruebas todo el tiempo. Intentando ejecutarlo con sentencias de depuración, etc. Escribir pruebas formaliza esto y proporciona un registro de las pruebas que tiene. De esa manera, puede verificar su funcionalidad con confianza sin omitir accidentalmente un caso de prueba que recordaba a la mitad del proceso de desarrollo.


Otra forma de abordar la introducción de esto como una característica de diagnóstico ... también conocida como prueba de encendido automático (POST) que puede estar enviando casi el código del cliente ... y no simplemente un montón de pruebas simples, que es lo que deberían ser las pruebas y las funciones.
JustinC

Además, evite los antipatrones TDD .
Gary Rowe

44
Misko Hevery también tiene algunos videos geniales en YouTube sobre cómo escribir código comprobable que me pareció invaluable. youtube.com/watch?v=acjvKJiOvXw
Despertar el

"Asegúrese de que las pruebas tengan un nivel de visibilidad", esto es fundamental para el éxito. Si nadie puede ver cómo están funcionando sus pruebas, no verán el valor. Las pruebas deben ejecutarse en el check-in automáticamente como parte de una integración continua y luego informarse. Trabajo en Tesults ( tesults.com ) y la razón por la que existe es por la gran visibilidad que proporciona la prueba de impacto.
Habilidad M2

77

En muchos sentidos, estoy de acuerdo con su equipo.

  1. La mayoría de las pruebas unitarias son cuestionables en valor. Dado que la gran mayoría de las pruebas parecen ser demasiado simples.

  2. Es mucho más difícil escribir un buen código comprobable que solo el código de trabajo. Hay un gran porcentaje de la comunidad de desarrolladores que cree en hacer que funcione, en comparación con la calidad del código / diseño en sí misma. Y un porcentaje aún mayor que ni siquiera sabe qué es el código de calidad.

  3. Puede llevar mucho más tiempo escribir el código de prueba de la unidad que el código real en sí.

  4. Descubrir cómo probar adecuadamente el código más complicado (es decir, lo que realmente le interesa probar a fondo) está más allá de las capacidades de muchos desarrolladores.

  5. Mantener las pruebas unitarias lleva demasiado tiempo. Pequeños cambios pueden tener grandes efectos de onda. El objetivo principal de las pruebas unitarias automatizadas es averiguar si los cambios rompieron el código. Sin embargo, el 99% del tiempo lo que termina rompiendo son las pruebas y no el código.

Con todos los problemas anteriores, todavía no hay una mejor manera de poder hacer cambios en el código y tener cierto nivel de confianza en que algo no se rompió de forma inesperada que automatizar sus pruebas.

Algo de lo anterior se puede aliviar hasta cierto punto al no seguir el libro de texto de las pruebas unitarias.

Muchos tipos de diseños / aplicaciones se prueban mejor mediante la automatización de pruebas a nivel de módulo / paquete. En mi experiencia, la mayoría de los errores de codificación no se deben a que el código de una clase se codificó incorrectamente, sino porque el codificador no entendió cómo se suponía que su clase debía funcionar con otras clases. He visto mucho dinero en este tipo de pruebas. Pero una vez más, estas pruebas son más difíciles de escribir que las pruebas unitarias (nivel de clase).

Realmente se reduce a si los desarrolladores creen en el proceso o no. Si lo hacen, escribirán buenas pruebas unitarias, encontrarán errores pronto y serán defensores. Si no lo hacen, entonces sus pruebas unitarias serán en general inútiles y no encontrarán ningún error y su teoría de que las pruebas unitarias son inútiles se demostrará cierta (en sus mentes).

La conclusión es que nunca he visto el enfoque completo de las pruebas unitarias automatizadas en más de un par de meses, pero la idea de las pruebas unitarias automatizadas aún persiste, aunque somos selectivos en lo que realmente necesita pruebas. Este enfoque tiende a tener menos críticas y es más aceptado por todos los desarrolladores que por unos pocos.


24
Tiendo a estar de acuerdo con esto. Nos hemos acostumbrado a hacer pruebas solo después de que algo se rompe (incluso si esa ruptura fue durante el desarrollo). Nunca por adelantado, toma demasiado tiempo por muy poca recompensa.
Izkata

55
@Izkata, el otro enfoque que he visto con éxito es escribir una cantidad relativamente pequeña de pruebas de alto nivel llamando al Frobinate()método de nivel superior (en lugar de las docenas de métodos más pequeños llamados debajo de él) después de que el sistema haya sido verificado por otros medios para servir como prueba de humo que ninguno de los cambios de nivel inferior rompió nada. En general, estas pruebas utilizaron los mismos datos que formaban parte de la libra en las pruebas de usuario del teclado proporcionadas para que el cliente pueda ver que el sistema está haciendo lo que quiere. Posteriormente, las herramientas de cobertura de código pueden identificar dónde aún no se están cubriendo los casos límite.
Dan Neely

3
No dije "pruebas automatizadas completas", dije "pruebas unitarias completas". Gran diferencia. He usado pruebas automatizadas a nivel de módulo durante al menos una década. Las pruebas unitarias son a nivel de clase. Creo que obtengo más por el dinero cuando pruebo clases que se supone que funcionan juntas, en lugar de individualmente. Sin embargo, incluso allí, todavía usamos un enfoque pragmático y elegimos selectivamente qué / dónde escribir pruebas automatizadas.
Dunk

2
Sin una buena cobertura de prueba unitaria, ¿cómo refactoriza? O sin refactorizar, ¿cómo evita que el código se degenere gradualmente y no se pueda mantener?
Kevin Cline

1
@Leonardo No lo hicieron, estaban demasiado asustados para cambiar algo. O ahorraron toda esa deuda técnica y la apartaron unas semanas / meses después para abordarla de una vez.
GraemeF

33

El principal culpable es burlarse / tropezar la base de datos o cualquier cosa que no sea simple.

Y ahí está tu problema.

Todos hacen buenos comentarios sobre cómo integrar las pruebas unitarias en su entorno. Cómo obligar a las personas a hacerlo lo suficiente como para que vean el valor práctico y se "pegue". Pero si es muy doloroso hacerlo y / o no proporciona ningún beneficio, no se mantendrá.

Cortar una base de datos debería ser muy simple. En lugar de que su interfaz vaya a algún respaldo de base de datos para proporcionar sus resultados, inserte un simple objeto codificado. Si no puede hacer eso, su diseño / arquitectura tiene problemas. Su código asume que va a una base de datos, o no tiene la abstracción de la interfaz para variarla.

Esto no es simplemente una cuestión de prueba / calidad. Tan pronto como desee cambiar los proveedores de bases de datos, o ir a la nube en su lugar, o admitir aplicaciones móviles no conectadas, su diseño simplemente falla. Si no puede soportar los casos de flexibilidad más simples, ciertamente no puede soportar las cosas más complejas que su negocio inevitablemente requerirá.


44
Los valores de retorno de la base de datos de codificación rígida de un pequeño trozo de un objeto simulado son una buena manera de aislar la prueba de cualquier cosa en la base de datos que pueda cambiar y romper el código (por ejemplo, renombrar columnas). Es apropiado bajo ciertas circunstancias, pero es importante mantener una base de datos de prueba temporal fácil de usar a menos que desee que las cosas se rompan algún día cuando la cambie. Si se rompe el código cuando intercambiar la base de datos, eso es un fallo del código que la prueba debe ser la captura (y si se quiere evitar que, tendrá que ejecutar el banco de prueba en múltiples bases de datos.)

8
@fennec: las pruebas unitarias no están ahí para probar la base de datos, están ahí para probar el código que depende de los valores de la base de datos para funcionar.
Telastyn

3
Todo bien hasta que esté probando el código que manipula la base de datos. : P que, para mucha gente, es mucho de su código.

44
@fennec: es un poco más complejo que simple cortar la interfaz para asegurarse de que sus escrituras estén escribiendo el objeto correcto. Solo se vuelve difícil cuando tus clases intentan enviar SQL directamente a tu interfaz (léase: tienes un diseño horrible).
Telastyn

55
@Telastyn tal vez estoy malinterpretando, pero eventualmente alguna clase necesita ensuciarse y escribir el SQL o escribir el archivo o enviar los datos o la interfaz con la GPU. La mayoría de las abstracciones tienen fugas inevitables en algún nivel; son simplemente pragmáticos y no necesariamente horribles.
Aprendiz de cola

21

Debe comenzar con algo pequeño, simple de automatizar y de alto valor. Tire hacia abajo un poco de fruta dulce y baja, y podrá vender el proceso. Muestre cómo ahorró a alguien una llamada nocturna o un fin de semana. Entonces puedes expandirte desde allí.

Para realizar bien las pruebas automatizadas, necesita a alguien que sea un recurso y un evangelista, y que haya comprado en las administraciones de nivel superior.

Trate su desarrollo de prueba automatizado como cualquier otro proyecto ágil. Produzca pruebas completas de forma regular.

Agregando desde el comentario: Eso es más un problema de gestión. ¿Se considera "hecho" el código antes de documentarlo? Antes de que se registre? ¿Antes de que incluya y pase las pruebas unitarias?

Cómo abordas esto realmente depende de tu rol. ¿Eres un compañero? Si es así, muestre a los demás cómo hace que su código sea más fácil de reutilizar y mantener. ¿Eres un líder? Elija a su programador que tenga más problemas de código y ayúdelo a agregar pruebas para evitar esos problemas. ¿Eres un jefe? Establezca como un estándar que "el código no se realiza hasta que las pruebas unitarias estén en curso y aprobadas".


17
"Saque un poco de fruta dulce y baja, y podrá vender el proceso": Creo que ya llegaron a esta etapa (vieron ventajas potenciales en el uso de pruebas unitarias) y es por eso que estaban convencidos de darlo. un intento. El problema es más bien cómo escalar las frutas bajas para hacer pruebas unitarias sistemáticamente. El único proyecto en el que trabajé en el que las pruebas unitarias se utilizaron sistemáticamente tenía más código de prueba unitaria que el código real del producto. Si un equipo no está preparado para pasar más tiempo codificando pruebas unitarias que el código de la aplicación real, entonces el enfoque de IMO probablemente no funcionará.
Giorgio

44
Eso es más un problema de gestión. ¿Se considera "hecho" el código antes de documentarlo? Antes de que se registre? ¿Antes de que incluya y pase las pruebas unitarias? Cómo abordas esto realmente depende de tu rol. ¿Eres un compañero? Si es así, muestre a los demás cómo hace que su código sea más fácil de reutilizar y mantener. ¿Eres un líder? Elija a su programador que tenga más problemas de código y ayúdelo a agregar pruebas para evitar esos problemas. ¿Eres un jefe? Establezca como un estándar que "el código no se hace hasta que las pruebas de la unidad estén y pasen.
Salte a Huffman

1
@SkipHuffman, su comentario debe agregarse como una edición a la respuesta actual.
radu florescu

15

Sigue estas reglas básicas. Pruebas:

  1. debe correr regularmente! Puede realizar pruebas en cada compilación, antes / después de cada registro, o solo todas las mañanas. La activación automática es muy preferible a la activación manual. Porque si bien, en teoría, puede hacer que todos en el equipo sean responsables de garantizar que ejecuten las pruebas, si no está automatizado, ¡probablemente no suceda con la suficiente frecuencia! Y si no ejecuta sus pruebas con la frecuencia suficiente, ambos encontrarán los errores demasiado tarde y alentarán muchas pruebas rotas, lo que lleva al punto 2:

  2. Todavía tendrá éxito si esas pruebas, que ahora se ejecutan regularmente, no se interponen en el camino . Con lo cual queremos decir pruebas:

    a. ¡No debe tardar demasiado en ejecutarse (subjetivamente) por el valor que proporcionan! Haga que sus pruebas sean extremadamente rápidas. ¡No permita que las personas verifiquen las pruebas que serán una pérdida de tiempo para dejarlas correr!

    si. No debe ser poco confiable. Evite las pruebas multiproceso si es posible. Aplique prácticas de ingeniería a sus pruebas al igual que su otro código: en particular, ¡revise el código de sus pruebas!

    C. No debe ser más difícil de arreglar y mantener que el código real probado. Su velocidad de codificación realmente va a succionar si un pequeño cambio de una línea en su base de código requiere que arregle 10 pruebas diferentes.

Y finalmente, la regla número 3. Las pruebas no solo deben dejar de proporcionar un valor negativo, como en la regla 2, deben proporcionar un valor positivo. Pruebas ...

  1. ¡debe estar diciéndote algo que te importa cuando fallan! (¡No hay pruebas con mensajes de error oscuros, o simplemente le dan quejas ridículas como 'olvidó ejecutar la prueba en una máquina con Windows 2008', por favor!).

Una forma popular de violar la regla # 3 es probar lo incorrecto . Esto a veces se debe a que una prueba es demasiado grande o demasiado desenfocada. Pero generalmente proviene de no probar algo que le interesará a un cliente y de probar detalles de implementación irrelevantes. (Pero a veces, probar los detalles de implementación también es una prueba eficiente: en mi opinión, solo se necesita práctica para decidir cuál)

Conclusión: estas reglas básicas lo guían en la dirección general de una disciplina de prueba sostenible , que es lo que ansía desesperadamente. Al realizar la prueba, pregúntese si esta prueba es realmente sostenible y sostenible. Recuerda:

  • Si las pruebas no son sostenibles, caen en desuso y, por lo tanto, se convierten en un esfuerzo perdido
  • si las pruebas no son sostenibles, deja de hacer pruebas y tu equipo deja de mejorar en las pruebas. Y, punto final:

La prueba es realmente difícil. Debes esperar que las pruebas de tu equipo básicamente apestan cuando comienzas a escribir las pruebas . No te desanimes. No tire las pruebas de edad, siempre que muestre que chupar y es insostenible.


12

1. ¿Realmente funciona?

Sí, lo hace, si se hace correctamente. El punto es que los probadores necesitan ajustar y extender sus scripts automáticos después de que los ingenieros implementen nuevas características.

2. Nadie tiene realmente experiencia ni sabe cómo realizar pruebas automatizadas de manera adecuada.

Obtenga un consultor (alguien que sepa cómo se hace correctamente). O invierta más tiempo. La alternativa es tener un equipo de prueba más grande, que haga la misma prueba manualmente (que es propenso a errores).

3.Tenemos un gran proyecto con 20 desarrolladores experimentados que trabajan en él. Por lo tanto, debería ser un excelente entorno para introducir pruebas unitarias / pruebas de integración. ¿Por qué no funciona para nosotros? ¿Cómo lo hiciste funcionar en tu empresa?

No los llamaría "buenos desarrolladores experimentados" si se niegan a hacer pruebas unitarias. Hay muchos artículos excelentes sobre los beneficios positivos de las pruebas (tanto de pruebas unitarias como de integración), y al final se reduce a cuánto le cuesta a su empresa un error . Por ejemplo, trabajo en una empresa donde la calidad es importante, por lo tanto, las pruebas de unidad e integración son inevitables. ¡Puede encontrar fácilmente muchos artículos que indican que las pruebas unitarias solo reducen la cantidad de errores en un 30%! (En realidad, está en el rango de 20-90%, en promedio 30%, pero aún es mucho).

Para que funcione en su empresa, contrate a un consultor o asigne esta tarea a un ingeniero superior (le tomará un tiempo hacerlo). Y luego, obligar a todos a cumplir las reglas.


20
Tengo que señalar que prácticamente TODO funciona "si se hace correctamente". Sin embargo, eso no es muy útil para todos, pero una minoría muy pequeña de cualquier población. Para que un proceso pueda realmente afirmar que funciona, también debe funcionar cuando se hace "más o menos".
Dunk

11
Señalaré que la generalización excesiva de la situación de todos a la suya (es decir, no los llamaría "buenos desarrolladores experimentados" ... la calidad importa) no solo demuestra su falta de amplitud de experiencia, sino que no es muy productiva. Cada industria tiene su propia definición de "obras"; y qué grado de prueba debe hacerse a nivel de unidad, integración y sistema. Muchos desarrolladores "excepcionalmente buenos" se han dado cuenta de que las pruebas unitarias ofrecen poco dinero en comparación con las pruebas de integración automatizadas. También se dan cuenta de que esto probablemente solo se aplica a su industria específica.
Dunk

11
"No los llamaría 'buenos desarrolladores experimentados' si se niegan a hacer pruebas unitarias". Esto es solo la falacia de No True Scotsman. La industria del software se llevó bien durante décadas sin pruebas unitarias y la mayoría de esa industria continúa llevándose bien sin ella hoy. Puede ser una buena idea, pero simplemente no es obligatorio.
Noah Yetter

1
"no perder el tiempo en pruebas unitarias": lo reformularía como "no perder el tiempo en pruebas unitarias inútiles". La unidad ciega que prueba todo puede resultar en una gran pérdida de tiempo.
Giorgio

1
@Dunk Realmente odio todo el fenómeno TDD de prueba primero, pero no puedo estar de acuerdo con tu primera declaración. O estás haciendo algo bien o no lo estás haciendo. Puede hacer una prueba de unidad bien y tal vez ver los méritos de la misma, pero nunca verá los méritos de nada hecho a medias.
Erik Reppen el

10

Son muchas las razones por las cuales la introducción de pruebas automatizadas puede fallar. Creo que se reduce al hecho de que los programadores tienden a no cambiar sus hábitos de codificación y no son totalmente capaces de adoptar las pruebas unitarias.

Muchas personas que desean comenzar con las pruebas automatizadas intentan presentarlas para una base de código existente. Intentarán escribir pruebas de integración que prueben mucha funcionalidad de la aplicación existente a la vez. Tales pruebas de integración son notoriamente demasiado difíciles y demasiado caras de mantener. Consejo: Introducir pruebas automatizadas para una nueva base de código.

Las pruebas unitarias son buenas pruebas para ser automatizadas. Todo lo anterior (pruebas de integración, pruebas de componentes, pruebas de sistema) también se puede probar automáticamente, pero la relación costo-beneficio disminuye rápidamente a medida que se prueba más funcionalidad a la vez. Este efecto negativo se amplifica si construye tales pruebas en una funcionalidad mal probada por unidad. Consejo: introduzca pruebas automatizadas en el nivel de prueba unitaria y cree pruebas de integración automatizadas sobre una base sólida de pruebas unitarias .

De los puntos anteriores, gran parte del éxito de las pruebas automatizadas depende de cuán efectivas sean las pruebas unitarias. Tiene pruebas unitarias efectivas si se siente productivo con las pruebas unitarias. Cuando las personas comienzan con las pruebas unitarias, tienden a adaptar su código existente y sus hábitos de codificación a las pruebas unitarias. Irónicamente, esta es la forma más difícil de aprender las pruebas unitarias. Además, las pruebas unitarias requieren que cambie la forma en que codifica (por ejemplo, aplicando los principios SOLID ). La mayoría de los programadores pronto dejan de escribir pruebas unitarias porque piensan que la curva de aprendizaje es demasiado empinada y les resulta incómodo ajustar las pruebas unitarias en torno a un código diseñado no tan comprobable. Consejo: Aprenda las pruebas unitarias desde cero con un nuevo código y lidie con el hecho de que necesita cambiar sus hábitos de codificación.

Hay muchos otros factores, pero descubrí que para la mayoría de los programadores es problemático cambiar su forma de codificar. El código escrito sin prueba simplemente se ve diferente. Si no puede comprimir su código en un diseño comprobable, probablemente no podrá escribir pruebas unitarias efectivas. Esto destruye el terreno para realizar pruebas automatizadas efectivas.

Lo experimenté yo mismo y ahora estoy feliz de trabajar en una empresa que introdujo con éxito las pruebas automatizadas. Podría escribir mucho más sobre los otros factores, pero creo que los hábitos de codificación y las pruebas unitarias son los más importantes. Afortunadamente, hay otros que tienen más experiencia que yo y llenan libros con sus conocimientos. Uno de estos libros es Brownfield Application Development en .NET, que realmente puedo recomendar, ya que está utilizando la pila de tecnología de Microsoft.


Introduce automated tests on the unit test level and build automated integration tests on a solid foundation of unit tests.+1
c69

10

Una cosa que no he visto claramente abordada en las respuestas anteriores es que las pruebas unitarias son esencialmente un bien público y un costo privado. He escrito una publicación de blog al respecto aquí .

Todo se reduce a que, si bien un conjunto de pruebas beneficia al equipo o a un desarrollador individual, escribir la prueba es un costo para el que lo hace, la mayoría de las veces.

En resumen, a menos que se escriba la prueba de alguna manera, y las respuestas anteriores enumeran varias formas diferentes de hacerlo, no hay razón para que un desarrollador individual haga esto.

En una empresa en la que he trabajado, escribir pruebas unitarias era una parte necesaria para ofrecer una función. El nuevo código no se aceptaba a menos que una prueba unitaria fuera parte de la confirmación o la nueva característica: hubo breves revisiones de código para cada "tarea" que se le dio a un desarrollador. Puede valer la pena implementar una política similar en su lugar de trabajo.


8

¡Es interesante que el negocio sea más pro-testing que los desarrolladores! Me parece que su mayor desafío será superar la resistencia de sus desarrolladores al cambio; necesitan redefinir su comprensión de sus trabajos para incluir pruebas unitarias.

Nada puede ayudarlo más que los primeros éxitos de las pruebas unitarias para ayudar a sus desarrolladores a superar su resistencia a escribir estas pruebas. Si los empujas a hacer algo nuevo, asegúrate de presionar primero por algo con una recompensa casi garantizada.

@SkipHuffman tocó esto, pero voy a decirlo directamente. Algunas cosas son mucho más adecuadas para las pruebas automatizadas que otras. Para el primer paso, NO probaría la base de datos o la interfaz de usuario. La entrada de una base de datos puede ser extremadamente difícil de configurar y desmontar. Las pruebas de salida de la interfaz de usuario tienden a romperse rápidamente con cambios de apariencia que son completamente irrelevantes para sus pruebas.

Lo que yo llamaría "middleware" es perfecto para pruebas unitarias. Código que ha definido claramente las condiciones de entrada y salida. Si sigue el principio DRY (No se repita), habrá escrito algunas pequeñas clases o funciones para resolver problemas recurrentes que son exclusivos de su aplicación.

Las pruebas unitarias son una gran herramienta para limitar el riesgo de cambiar los componentes internos existentes. Escriba pruebas unitarias antes de cambiar un componente interno que ha funcionado durante mucho tiempo. Estas pruebas demuestran que se preserva la funcionalidad que funciona actualmente. Cuando haya realizado su cambio y todas las pruebas unitarias pasen, sabrá que no ha roto nada "aguas abajo". Si encuentra un problema posterior, agregue una prueba de unidad para ello.

Ron Heifitz diría "abordar los conflictos en los valores que las personas tienen, o disminuir la brecha entre los valores que las personas defienden y la realidad que enfrentan. El trabajo adaptativo requiere un cambio en los valores, creencias o comportamiento". Después de superar la resistencia humana al cambio, puede ramificarse en áreas de prueba más difíciles según corresponda.


66
"superar la resistencia de sus desarrolladores al cambio": no todas las resistencias carecen de razón y no se debe evitar una discusión honesta utilizando el argumento de "resistencia al cambio".
Giorgio

7

Una cosa acerca de las pruebas automatizadas es que requiere que escribas código para que sea comprobable. Esto no es algo malo en sí mismo (de hecho, es bueno porque desalienta muchas prácticas que, por regla general, deben evitarse), pero si está tratando de aplicar pruebas unitarias al código existente, entonces lo más probable es que no lo sea. ha sido escrito de manera comprobable.

Cosas como singletons, métodos estáticos, registros, localizadores de servicios, etc. introducen dependencias que son muy difíciles de burlar. Las violaciones de la Ley de Demeter significan que demasiadas partes de su base de código saben demasiado sobre cómo funcionan otras partes de su base de código, introduciendo dependencias ocultas adicionales que pueden ser difíciles de romper. Todas estas cosas hacen que sea difícil aislar un módulo del resto de la base de código, y si no puede probar sus módulos de forma aislada, las pruebas unitarias pierden mucho de su valor. Si una prueba falla, es debido a una falla en la unidad bajo prueba, o debido a una falla en una de sus dependencias, o tal vez es porque los datos que se extraen a través de una fuente de datos dependiente no es lo que esperaba el escritor de la prueba. ? Si puedes'

La mayoría de las bases de código que he visto que no se crearon teniendo en cuenta las pruebas unitarias tienden a ser inherentemente inestables, ya que los codificadores tienden a centrarse en hacer que el código funcione como esperan, en lugar de hacer el trabajo necesario para mantener el acoplamiento suelto y las dependencias explícitas . El código escrito con las pruebas unitarias en mente tiende a verse muy diferente.

Muchas personas adoptan un enfoque ingenuo para las pruebas unitarias cuando comienzan a hacerlo por primera vez, piensan que pueden escribir una gran cantidad de pruebas para una base de código existente y todo será bueno, pero nunca funciona de esa manera debido a los problemas mencionados anteriormente Comienzan a descubrir que tienen que desordenar las cantidades de configuración en las pruebas unitarias para que se ejecuten, y los resultados a menudo son cuestionables porque la falta de aislamiento en el código significa que no puede rastrear qué causó una falla en la prueba. También tienden a comenzar a intentar escribir pruebas "inteligentes" que demuestren un aspecto muy abstracto de cómo debería funcionar el sistema. Esto tiende a fallar porque una prueba de unidad "inteligente" es una fuente potencial de errores en sí misma. ¿Falló la prueba debido a un error en el módulo probado, o debido a un error en la prueba? Una prueba debe ser tan insoportablemente simple que obviamente no hay posibilidad de que un error pueda estar escondido en ella. De hecho, las mejores pruebas rara vez tienen más de 2 líneas de largo, la primera línea indica a la unidad bajo prueba que haga algo, la segunda afirma que lo que hizo fue lo que se esperaba.

Si su equipo se toma en serio la adopción de pruebas unitarias, no sería prudente comenzar con un proyecto existente. Los proyectos existentes de su equipo probablemente no sean verificables sin una refactorización importante. Es mejor usar un nuevo proyecto como base para aprender sobre las pruebas unitarias, ya que tiene una pizarra limpia para trabajar. Puede diseñar la nueva base de código para favorecer la inyección de dependencias sobre singletons, registros y otras dependencias ocultas, puede escribirla para que dependa de interfaces en lugar de implementaciones, etc. También puede (y debe) escribir las pruebas junto con el código que se está probando, ya que escribir las pruebas después da como resultado pruebas unitarias que aseguran que el módulo probado haga lo que cree que debería estar hecho en lugar de las que prueban que sí lo hace. lo que dicen las especificaciones que debería hacer.

Una vez que haya ganado algo de confianza con las pruebas unitarias, su equipo probablemente comenzará a darse cuenta de las fallas en su código existente que serán obstáculos para las pruebas unitarias. Aquí es cuando puede comenzar a trabajar para refactorizar el código existente para hacerlo más comprobable. No sea ambicioso e intente hacer todo esto de una vez, o intente reemplazar un sistema que funciona con uno completamente nuevo, simplemente comience por encontrar los bits de la base de código que pueden probarse fácilmente (los que no tienen cualquier dependencia o donde las dependencias son obvias) y escriba pruebas para ellas. Sé que dije que escribir una prueba junto con el código es preferible a escribir pruebas después, pero incluso una prueba escrita más tarde todavía tiene valor como punto de partida. Escriba las pruebas como si no supiera nada sobre cómo funciona la clase, aparte de lo que sus especificaciones dicen que debería hacer. Cuando ejecuta las pruebas y obtiene fallas, las especificaciones o la implementación son incorrectas. Verifique ambos para determinar cuál está mal y actualice la prueba o el código en consecuencia.

Una vez que haya recogido la fruta baja, comienza su verdadero trabajo. Debe comenzar a encontrar las dependencias ocultas en su base de código y corregirlas, una a la vez. No se vuelva demasiado ambicioso en este punto, simplemente manténgase haciendo un módulo a la vez, o incluso un solo problema en un módulo, hasta que se solucionen los obstáculos para la prueba y pueda pasar al siguiente bit.

TL: DR: La mayoría de las personas piensan que las pruebas son fáciles y que puedes adaptarlas fácilmente al código existente. Ambos supuestos están equivocados. Si se embarca en un proyecto para obtener pruebas unitarias en sus proyectos con estos dos hechos en mente, es más probable que tenga éxito.


pista: pon el TL; DR: en la parte superior - ¡Tuve que leer toda tu publicación solo para llegar a ella! (lo cual un poco derrota el punto)
gbjbaanb

4
  • ¿Hay alguien en su empresa con amplia experiencia haciendo pruebas automatizadas?

Si no, las iniciativas de prueba automatizadas probablemente fracasarán. Las pruebas automatizadas son una habilidad, como muchas otras habilidades en programación, y si no tienes a nadie con experiencia en hacerlo, no es fácil saber si una prueba automatizada es una buena prueba automatizada con valor real, o una mala que lo hará falla al azar / requiere actualizaciones frecuentes / en realidad no ejerce ningún código interesante.

  • ¿Alguien tiene poder de liderazgo? ¿Pueden exigir un cambio?

Si nadie escucha, no importa si dicen que la prueba no es buena. (Tenga en cuenta que el poder de liderazgo no tiene que ser formalizado. Tener un equipo que se preocupe también es bueno).

  • ¿Está desarrollando herramientas y procesos para hacer que las pruebas automatizadas sean más fáciles de implementar e integrar en el ciclo de desarrollo?

Los desarrolladores son flojos. Debes hacer que las cosas que quieres que hagan sean fáciles de lograr, y las cosas que no quieres que hagan más difíciles de lograr. Debe asegurarse de que las bibliotecas de prueba faciliten las tareas asociadas con la configuración de prueba y el desmontaje, especialmente la configuración relacionada con el entorno, como bases de datos de prueba o similares. (La burla de la base de datos se discute en algunos de estos comentarios, pero debe usarse con precaución. Una base de datos real debe ser fácil de implementar y le permite probar la interacción de los componentes y los ciclos de vida del proceso, a menudo más importantes y más efectivos que las pruebas unitarias un accesor de datos individual).

También debe asegurarse de que sus IDEs tengan una buena manera de iniciar el conjunto de pruebas. Debe ejecutar el conjunto de pruebas con frecuencia para que las personas se den cuenta de cuándo falla en lugar de dejar que permanezca en la miseria. Los desarrolladores también responden bien a los comentarios, por ejemplo, un sistema de integración automatizado que revierte sus cambios si han roto una prueba. O, mejor, comentarios positivos: un sistema de integración automatizado que detecta errores y evita que rompas cosas.


No creo que sea justo decir que los desarrolladores son flojos. Tal vez ese sea el caso en su experiencia, pero seguramente no es una verdad universal.
Sam

4

Primero, si sus desarrolladores no ven el valor de sus pruebas, entonces probablemente sea porque sus pruebas no son valiosas, no porque sus desarrolladores sean ciegos a su valor o al valor de las pruebas en general. Entre sus evangelistas, hay una tendencia a creer que el desarrollo impulsado por pruebas no puede fallar, simplemente puede ser fallado por desarrolladores flojos, flojos. Creo que esto está mal y es contraproducente.

Cuando me presentaron el desarrollo basado en pruebas, significaba, efectivamente, escribir una prueba para verificar que un método que nunca fallará nunca falla. Lo cual es bueno, al principio, porque obtienes un hermoso cheque verde y una sensación de logro. Más tarde, después de refactorizar el código, tiene decenas de X rojas exasperantes, ninguna de las cuales dice nada más que el código ha cambiado, que las pruebas ya no son válidas y que perdió mucho tiempo escribiéndolas.

Quieres evitar eso.

Desde entonces, he adoptado un enfoque diferente para las pruebas. En lugar de un par de implementación de interfaz , tengo una interfaz, implementación, prueba triple . La interfaz especifica el comportamiento, la implementación realiza el comportamiento, la prueba verifica el comportamiento.

Supongo que parece obvio, pero para mí, distingue entre el código que debe probar que funciona como se especifica y el código que puede probar tanto o tan poco como considere apropiado. El código que debe probar es la interfaz que ofrece al exterior; El resto es asunto tuyo.

En este caso, les preguntaría a los desarrolladores si ven una división natural en el código donde este tipo de prueba sería apropiado. ¿Existe una interfaz que implemente el Equipo A y el Equipo B? En ese caso, le interesa al Equipo B asegurarse de que la interfaz se comporte como ellos esperan. Pídale al equipo B que escriba una prueba para ello, luego dígale al equipo A que se asegure de que su implementación se ajuste a la prueba; o, si no es así, y no lo hace intencionalmente, para discutir el cambio inesperado con el otro equipo, para que puedan prepararse para ello.

Creo que esto ilustraría el valor de la prueba. No es un fin en sí mismo, a pesar de los hermosos cheques verdes. Existe para hacer explícita la promesa hecha por un desarrollador a otro, y para garantizar que la promesa se cumpla a satisfacción de ambos.


1
Me gusta el código que puedo leer mejor que el código que alguien consideró necesario probar hasta las minucias así.
Erik Reppen

1
Los encantadores tics verdes que siento son el problema: hace que las pruebas se conviertan en algún tipo de juego.
gbjbaanb

2

Agregar muchas pruebas unitarias a un gran proyecto preexistente es un trabajo duro. Si ya ha encontrado un buen marco de burla que funciona para usted, entonces debería haber resuelto el problema más difícil.

Sugiero intentar agregar pruebas a medida que agrega características / corrige errores. La corrección de errores es la más fácil. Escriba una prueba que falle debido a su error y luego corríjala. Al mismo tiempo, probablemente te encuentres escribiendo algunas pruebas más simples que sí pasan. Por supuesto, realmente desea utilizar un código pequeño y fácil de probar para esto.

Una vez que las personas comienzan a acostumbrarse a escribir pruebas para las cosas más fáciles, es de esperar que escriban su código para que sea más comprobable.

También recomendaría que mida la cobertura del código de sus pruebas (he usado cobertura para Java en el pasado). Querrá tener un servidor de integración continua que ejecute las pruebas y produzca las métricas de forma regular (todos los días, cada registro). Si sus compañeros desarrolladores están interesados, entonces querrán ver que la cobertura aumente con el tiempo y puedan ver los agujeros de cobertura en algunos de sus


2

Creo que puede que tengas que jugar el juego largo. Una cosa que puede hacer para obtener cierta aceptación es intentar probar exhaustivamente la unidad de la siguiente característica que escriba y luego realizar un seguimiento de la cantidad de errores a lo largo del tiempo. Con suerte, descubrirá que los errores más importantes se detectarán desde el principio (especialmente si combina esto con el diseño impulsado por prueba) y el número de regresiones debe ser muy bajo. Después de un período de tiempo, digamos 1 año, compare las estadísticas con características no probadas de unidad de complejidad similar. Si puede demostrar que la cantidad de nuevos errores y regresiones es considerablemente menor, entonces ha proporcionado una justificación financiera para ello y se hace más difícil de ignorar para el equipo del producto.

Tuve una situación en la que pude usar TDD y pruebas de unidad para una característica importante. Después del final de la fase de desarrollo, no se informó un solo error en más de 5 años. Cuando se solicitó una nueva y arriesgada mejora, pude implementarla y detectar todas las regresiones en las pruebas unitarias.


1

En mi opinión, muchos equipos subestiman en gran medida el valor de las pruebas unitarias debido a varios factores, muchos de los cuales ya se destacan en las respuestas.

A menudo, los desarrolladores están bajo presión para "hacer las cosas", por lo que demostrar que un bloque de código funciona es una prueba suficiente para el cliente. Esto casi siempre se aplica a la empresa de consultoría y al control de calidad impulsado por el ser humano: si el cliente no requiere pruebas unitarias y considera que una demostración en vivo es suficiente, entonces el cliente ha fallado por completo ya que firmará la aprobación del código que podría ocultar fallas.

A menudo los desarrolladores están frustrados. Ser programador es un trabajo difícil: terminar una tarea e ir a la siguiente es satisfactorio, por lo que todos quieren darse prisa y terminar. Hasta que los atropella un autobús con un error importante que aumenta meses después del control de calidad original. En este escenario, el control de calidad automatizado y continuo es un problema de la administración en lugar de los desarrolladores (todavía se les pagará por su trabajo, tal vez horas extras).

Pero hay una excepción

Creo firmemente que la aceptación del modelo de prueba automatizado es una función de la "humanidad" de las pruebas que se realizan. Si está probando un módulo web con una interfaz, es más probable que, a pesar de herramientas como Selenium, complete el formulario usted mismo, vea el resultado y crea en el determinismo. Olvidarás volver a ejecutar las pruebas más tarde o simplemente serás demasiado flojo para volver a hacer pruebas antiguas, y es por eso que a veces se descubren errores más tarde. Para aprovechar esto, se ha demostrado que una fuerte modularización del código y reglas estrictas sobre "modificar el código antiguo" en un entorno bancario (en mi experiencia laboral personal).

En cambio, si el desarrollador está a cargo de desarrollar un módulo de datos altamente automatizado y de gran volumen, será más probable que escriba pruebas unitarias exhaustivas y las envíe a los lotes de prueba. Esto se debe a que llenar una gran carga útil XML con datos convertidos de una fuente de datos externa (simulada o no) no es un trabajo propenso a los humanos. Algunos desarrolladores de pruebas eventualmente construirán una interfaz pequeña y divertida para este tipo específico de pruebas. Cuando trabajaba en mi tesis de maestría, estaba trabajando en un bus de registro que manejaba más de 6000 mensajes de syslog por segundo y tenía que medir la pérdida y corrupción de paquetes: naturalmente escribí pruebas de unidad y estrés para casi todos los componentes, especialmente el analizador Syslog.

Para que los desarrolladores sean más propensos a las pruebas unitarias

Creo que deben ser obligados a hacerlo. Si es un cliente inteligente, necesitará que sus consultores ejecuten el conjunto de pruebas completo en cada control de calidad. Si eres un buen líder de equipo, puedes pensar en asignar la siguiente tarea a un desarrollador inteligente: crear una plataforma de prueba interna. Eso no tiene nada que ver con la plataforma de efectos internos antipatter, sino que es un conjunto de clases auxiliares, simulacros de bases de datos, configuraciones, analizadores, convertidores, navajas suizas para ayudar a los desarrolladores a construir pruebas en poco tiempo.

Las plataformas de prueba actuales como NUnit son de uso general y le permiten verificar afirmaciones genéricas. El uso correcto de la inyección de dependencia y las fábricas específicas del proyecto ayudan a los desarrolladores a escribir menos código para las pruebas y ser más felices. Todavía no he tenido la oportunidad de experimentar esto en un proyecto completo, no puedo proporcionar comentarios de la vida real.


1

Las pruebas automatizadas son como el desarrollo de software. Lamentablemente, las personas que contrata para las pruebas están destinadas originalmente a escribir casos de prueba, planes, estrategias, seguir el proceso de revisión, probar manualmente y registrar errores.

Tan pronto como se les otorgan responsabilidades de prueba automatizadas, incluye una cierta cantidad de desarrollo de software. El problema aquí es que las pruebas automatizadas, independientemente de las herramientas que use (y, por el amor de Dios, no discuta este punto), necesitan mantenimiento y actualización a diario. A medida que los desarrolladores cambian el código,

  • debe asegurarse de que las pruebas se sigan ejecutando.
  • Debe asegurarse de que las pruebas no se eliminen ya que no se ejecutaron
  • sus métricas de prueba deben mostrar lo que ejecutó en la última compilación y esta compilación. Para garantizar que su número de casos de prueba no se reduzca.
  • los casos de prueba deben revisarse al igual que el desarrollo para garantizar que las personas no se equivoquen, un poco dividiendo 1 prueba en 2 solo para aumentar los números (algunas veces las pruebas se subcontratan, por lo que este seguimiento es importante)
  • una comunicación mucho más "saludable" entre el desarrollador y la prueba es importante
  • mantenga las non-functionalpruebas separadas, y no espere que se ejecuten diariamente, lleva tiempo mantenerlas actualizadas, y bien. Pero no te rindas, asegúrate de que se mantengan.

Fracasas por estas razones

  • Sus ingenieros de prueba son ingenieros de prueba manuales sin habilidades analíticas. no saben la diferencia entre a ify un whilebucle. Porque, francamente, ningún curso enseña pruebas automatizadas, solo enseñan pruebas manuales.
  • sus ingenieros de prueba están demasiado ocupados probando manualmente sus compilaciones y registrando errores para que pierdan el rastro de las pruebas automatizadas
  • sus gerentes de prueba no se preocupan por las métricas de las pruebas automatizadas, solo porque muestran datos no válidos (cuando comienza el proyecto), y no ponen esfuerzo o prioridad en las reuniones y standups diarios para enfatizar lo importante que es poner en funcionamiento la automatización
  • elige automatizar las pruebas para aplicaciones móviles, que tienen una vida útil muy corta. para cuando escriba, estabilice el conjunto de pruebas automatizadas que cambian los requisitos de su aplicación, en su lugar, debe concentrarse en probar sus servicios web que ejecutan su aplicación
  • no comprende que el equipo de prueba automatizado sigue el mismo hito: equipo de desarrollo, función completa, código completo, bloqueo de código y congelación de código.
  • no diferencia entre personas de pruebas manuales y personas de pruebas automatizadas.
  • A ambos se les paga el mismo salario e informan al mismo gerente, idealmente deben informar al gerente de desarrollo y sus salarios deben coincidir con los del desarrollo.
  • realmente piensas y crees que junit no es suficiente para desarrollar pruebas automatizadas :).

Estos son de mi experiencia trabajando para empresas que toman muy en serio las pruebas automatizadas y entienden que el desarrollo es importante como ingenieros de pruebas automatizadas. Y desde mi experiencia trabajando para personas que no saben, entiendan la diferencia, no importa cuánto les expliquen.


En el caso de las pruebas de unidad e integración, las personas que deberían escribir las pruebas son los desarrolladores, no los "ingenieros de prueba" separados. (Como está escrito en la pregunta, el control de calidad, es decir, los ingenieros de pruebas, en realidad ya utilizan pruebas de interfaz de usuario automatizados.)
Paulo Ebermann

Hablando de manera realista, cualquiera que escriba pruebas automatizadas debe tener habilidades de desarrollo.
Siddharth
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.