test telecomunicaciones historia example ejemplo driven development tdd agile

telecomunicaciones - test driven development o tdd



¿Cómo se hace TDD en una aplicación no trivial? (8)

He leído varios libros y sitios web sobre el tema de TDD, y todos tienen mucho sentido, especialmente el libro de Kent Beck. Sin embargo, cuando trato de hacer TDD, me encuentro mirando el teclado preguntándome cómo comenzar. ¿Hay algún proceso que uses? ¿Cuál es tu proceso de pensamiento? ¿Cómo identifica sus primeras pruebas?

La mayoría de los libros sobre el tema hacen un gran trabajo al describir lo que es TDD, pero no cómo practicar TDD en aplicaciones no triviales del mundo real. ¿Cómo se hace TDD?


Comienzo pensando en los requisitos.

Foreach UseCase

  1. analizar UseCase
  2. pensar en clases futuras
  3. anote los casos de prueba
  4. escribir pruebas
  5. probando e implementando clases (a veces agregando nuevas pruebas si me perdí algo en el punto 4).

Eso es. Es bastante simple, pero creo que lleva mucho tiempo. Aunque me gusta y me atengo a eso. :)

Si tengo más tiempo, trato de modelar algunos diagramas secuenciales en Enterprise Architect.


Es más fácil de lo que piensas, en realidad. Simplemente usa TDD en cada clase individual. Todos los métodos públicos que tenga en la clase deben probarse para ver todos los resultados posibles. Por lo tanto, los ejemplos TDD de "prueba de concepto" que ve también se pueden usar en una aplicación relativamente grande que tiene muchos cientos de clases.

Otra estrategia de TDD que podría usar es simular las ejecuciones de pruebas de aplicación, al encapsular el comportamiento de la aplicación principal. Por ejemplo, he escrito un marco (en C ++, pero esto debería aplicarse a cualquier lenguaje OO) que representa una aplicación. Hay clases abstractas para la inicialización, el runloop principal y el apagado. Entonces mi método main () se ve así:

int main(int argc, char *argv[]) { int result = 0; myApp &mw = getApp(); // Singleton method to return main app instance if(mw.initialize(argc, argv) == kErrorNone) { result = mw.run(); } mw.shutdown(); return(result); }

La ventaja de hacer esto es doble. En primer lugar, todas las funciones principales de la aplicación se pueden compilar en una biblioteca estática, que luego se vincula tanto con el conjunto de pruebas como con el archivo stub main.cpp. En segundo lugar, significa que puedo simular "ejecuciones" completas de la aplicación principal creando matrices para argc & argv [], y luego simulando lo que sucedería en main (). Usamos este proceso para probar muchas funcionalidades del mundo real para asegurarnos de que la aplicación genere exactamente lo que se supone que debe hacer dado un cierto corpus del mundo real de datos de entrada y argumentos de línea de comandos.

Ahora, probablemente se esté preguntando cómo cambiaría esto para una aplicación que tiene una GUI real, una interfaz basada en web o lo que sea. Para eso, simplemente diría usar maquetas para probar estos aspectos del programa.

Pero en resumen, mi consejo se reduce a esto: desglosa tus casos de prueba al nivel más bajo, luego comienza a mirar hacia arriba. Eventualmente, el conjunto de pruebas los juntará a todos y obtendrá un nivel razonable de cobertura de prueba automatizada.


Estoy de acuerdo en que es especialmente difícil iniciar el proceso.

Por lo general, trato de pensar en el primer conjunto de pruebas, como un guión de la película, y tal vez solo la primera escena de la película.

Actor1 le dice a Actor2 que el mundo está en problemas, Actor2 entrega un paquete, Actor1 desempaqueta el paquete, etc.

Obviamente, este es un ejemplo extraño, pero a menudo encuentro que visualizar las interacciones es una buena manera de superar esa joroba inicial. Existen otras técnicas análogas (historias de usuario, tarjetas RRC, etc.) que funcionan bien para grupos más grandes, pero parece que estás solo y puede que no necesites la sobrecarga adicional.

Además, estoy seguro de que lo último que quieres hacer es leer otro libro, pero los chicos de MockObjects.com tienen un libro en las etapas iniciales del borrador, actualmente titulado Growing Object-Oriented Software, Guided by Tests . Los capítulos que se revisan actualmente pueden brindarle más información sobre cómo iniciar TDD y continuarlo durante todo el proceso.


No creo que realmente deba comenzar con TDD. En serio, ¿dónde están tus especificaciones? ¿Ya acordó un diseño general general / aproximado para su sistema, que puede ser apropiado para su aplicación? Sé que TDD y Agile desalientan Big Design Up-Front, pero eso no significa que no se debe hacer Design Up-Front primero antes de TDD en la implementación de ese diseño.


Yo solia tener el mismo problema. Solía ​​comenzar la mayoría del desarrollo iniciando un diseñador de ventanas para crear la interfaz de usuario para la primera característica que quería implementar. Como la interfaz de usuario es una de las cosas más difíciles de probar, esta forma de trabajar no se traduce muy bien en TDD.

Encontré los documentos de objetos atómicos en Presenter First muy útiles. Todavía comienzo visualizando las acciones de los usuarios que deseo implementar (si tienes un caso de uso que es una gran manera de comenzar) y usando un modelo MVP o MVC-ish, comienzo a escribir una prueba para el presentador de la primera pantalla. Al burlar la vista hasta que el presentador funcione, puedo comenzar realmente rápido de esta manera. http://www.atomicobject.com/pages/Presenter+First aquí hay más información sobre cómo trabajar de esta manera.

Si está comenzando un proyecto en un idioma o marco que le es desconocido o que tiene muchos desconocidos, primero puede comenzar haciendo un pico. A menudo también escribo pruebas unitarias para mis picos, pero solo para ejecutar el código que estoy agregando. Hacer el pico puede darte información sobre cómo comenzar tu proyecto real. No se olvide de tirar su pico cuando comience su proyecto real


A veces no sabes cómo hacer TDD porque tu código no es "prueba amigable" (fácilmente comprobable).

Gracias a algunas buenas prácticas, sus clases pueden ser más fáciles de probar de forma aislada, para lograr pruebas unitarias reales.

Hace poco encontré un blog de un empleado de Google, que describe cómo puedes diseñar tus clases y métodos para que sean más fáciles de probar.

Aquí está una de sus conversaciones recientes que recomiendo.

Él insiste en el hecho de que debe separar la lógica de negocios del código de creación de objeto (es decir, para evitar mezclar la lógica con el "nuevo" operador), usando el patrón de Inyección de Dependencia. También explica cómo la Ley de Demeter es importante para el código comprobable. Se enfoca principalmente en el código Java (y Guice ), pero sus principios deberían aplicarse a cualquier lenguaje en realidad.


El problema es que estás mirando tu teclado preguntándote qué pruebas necesitas escribir.

En cambio, piense en el código que desea escribir, luego encuentre la primera parte pequeña de ese código, luego intente y piense en la prueba que lo forzaría a escribir ese pequeño fragmento de código.

Al principio, ayuda a trabajar en pedazos muy pequeños. Incluso en el transcurso de un solo día estarás trabajando en trozos más grandes. Pero cada vez que te quedes atascado solo piensas en la pieza de código más pequeña que quieras escribir a continuación, luego escribe la prueba para ello.


Lo más fácil es comenzar con una clase que no tenga dependencias, una clase que sea utilizada por otras clases, pero que no use otra clase. Luego debe tomar una prueba, preguntándose "¿cómo sabría si esta clase (este método) se implementa correctamente?".

Luego, podría escribir una primera prueba para interrogar a su objeto cuando no se haya inicializado, podría devolver NULO o lanzar una excepción. Luego puede inicializar (tal vez solo parcialmente) su objeto, y probar la prueba devuelve algo valioso. Luego puede agregar una prueba con otro valor de inicialización, debe comportarse de la misma manera. En ese momento, suelo probar una condición de error, como intentar inicializar el objeto con un valor no válido.

Cuando haya terminado con el método, va a otro método de la misma clase hasta que haya terminado con toda la clase.

Luego, podría elegir otra clase, ya sea otra clase independiente o clase que use la primera clase que haya implementado.

Si vas con una clase que depende de tu primera clase, creo que es aceptable que tu entorno de prueba, o tu segunda clase, cree una instancia de la primera clase, ya que se ha probado completamente. Cuando falla una prueba sobre la clase, debe poder determinar en qué clase reside el problema.

Si descubre un problema en la primera clase, o si se comportará correctamente bajo ciertas condiciones particulares, escriba una nueva prueba.

Si subes por las dependencias y piensas que las pruebas que estás escribiendo abarcan muchas clases para calificar como pruebas unitarias, entonces puedes usar un objeto simulado para aislar una clase del resto del sistema.

Si ya tiene su diseño, como indicó en un comentario en la respuesta de Jon LimJap, entonces no está haciendo un TDD puro, ya que TDD se trata de usar pruebas unitarias para permitir que surja su diseño.

Dicho esto, no todas las tiendas permiten el uso estricto de TDD, y usted tiene un diseño a mano, así que usemos y hagamos TDD, aunque sería mejor decir Prueba-Primero-Programación, pero ese no es el punto, ya que así es como yo comenzó TDD.