¿Cuándo escribes el código "real" en TDD?

147

Todos los ejemplos que he leído y visto en videos de capacitación tienen ejemplos simplistas. Pero lo que no veo es cómo hago el código "real" después de que me ponga verde. ¿Es esta la parte "Refactor"?

Si tengo un objeto bastante complejo con un método complejo, y escribo mi prueba y el mínimo necesario para que pase (después de que falla por primera vez, Rojo). ¿Cuándo regreso y escribo el código real? ¿Y cuánto código real escribo antes de volver a probar? Supongo que la última es más intuición.

Editar: Gracias a todos los que respondieron. Todas sus respuestas me ayudaron inmensamente. Parece que hay diferentes ideas sobre lo que estaba preguntando o confundido, y tal vez sí, pero lo que estaba preguntando era, digamos que tengo una solicitud para construir una escuela.

En mi diseño, tengo una arquitectura con la que quiero comenzar, Historias de usuarios, etc. A partir de aquí, tomo esas historias de usuario y creo una prueba para probar la historia de usuario. El usuario dice: "Tenemos personas que se matriculan en la escuela y pagan tarifas de inscripción" Entonces, pienso en una manera de hacer que eso falle. Al hacerlo, diseño una clase de prueba para la clase X (tal vez Estudiante), que fallará. Luego creo la clase "Estudiante". Tal vez "Escuela" no lo sé.

Pero, en cualquier caso, el diseño de TD me está obligando a pensar en la historia. Si puedo hacer que una prueba falle, sé por qué falla, pero esto presupone que puedo aprobarla. Se trata del diseño.

Comparo esto con pensar en la recursión. La recursión no es un concepto difícil. Puede ser más difícil seguirlo en la cabeza, pero en realidad, lo más difícil es saber cuándo "se rompe" la recursión, cuándo parar (mi opinión, por supuesto). Así que tengo que pensar qué se detiene. La recursividad primero. Es solo una analogía imperfecta, y supone que cada iteración recursiva es un "paso". De nuevo, solo una opinión.

En la implementación, la escuela es más difícil de ver. Los libros de contabilidad numéricos y bancarios son "fáciles" en el sentido de que puede usar aritmética simple. Puedo ver a + b y devolver 0, etc. En el caso de un sistema de personas, tengo que pensar más en cómo implementar eso. Tengo el concepto de reprobar, aprobar, refactorizar (principalmente debido al estudio y esta pregunta).

Lo que no sé se basa en la falta de experiencia, en mi opinión. No sé cómo dejar de inscribir a un nuevo estudiante. No sé cómo fallarle a alguien que escribe un apellido y se guarda en una base de datos. Sé cómo hacer un + 1 para matemáticas simples, pero con entidades como una persona, no sé si solo estoy probando para ver si recupero una identificación única de la base de datos o algo más cuando alguien ingresa un nombre en un base de datos o ambos o ninguno.

O, tal vez esto muestra que todavía estoy confundido.

johnny
fuente
193
Después del TDD la gente se va a casa a pasar la noche.
hobbs
14
¿Por qué crees que el código que escribiste no es real?
Goyo
2
@RubberDuck Más que las otras respuestas. Estoy seguro de que lo consultaré pronto. Todavía es un poco extraño, pero no voy a renunciar a ello. Lo que dijiste tenía sentido. Solo estoy tratando de que tenga sentido en mi contexto o en una aplicación comercial normal. Tal vez un sistema de inventario o similar. Tengo que considerarlo. Sin embargo, estoy agradecido por tu tiempo. Gracias.
johnny
1
Las respuestas ya dan en el clavo, pero siempre y cuando todas sus pruebas estén aprobadas y no necesite ninguna prueba / funcionalidad nueva, se puede suponer que el código que tiene está terminado, el revestimiento de barras.
ESR
3
Hay una suposición en la pregunta que puede ser problemática en "Tengo un objeto bastante complejo con un método complejo". En TDD, primero escribe sus pruebas para comenzar con un código bastante simple. Esto lo obligará a codificar una estructura fácil de probar que deberá ser modular. Por lo tanto, se creará un comportamiento complejo combinando objetos más simples. Si termina con un objeto o método bastante complejo, entonces es cuando refactoriza
Borjab

Respuestas:

242

Si tengo un objeto bastante complejo con un método complejo, y escribo mi prueba y el mínimo necesario para que pase (después de que falla por primera vez, Rojo). ¿Cuándo regreso y escribo el código real? ¿Y cuánto código real escribo antes de volver a probar? Supongo que la última es más intuición.

No "regresas" y escribes "código real". Todo es código real. Lo que debe hacer es regresar y agregar otra prueba que lo obligue a cambiar su código para poder aprobar la nueva prueba.

¿En cuanto a cuánto código escribes antes de volver a probar? Ninguna. Escribe un código cero sin una prueba fallida que lo obliga a escribir más código.

¿Ves el patrón?

Veamos (otro) ejemplo simple con la esperanza de que ayude.

Assert.Equal("1", FizzBuzz(1));

Fácil peazy.

public String FizzBuzz(int n) {
    return 1.ToString();
}

No es lo que llamarías código real, ¿verdad? Agreguemos una prueba que fuerce un cambio.

Assert.Equal("2", FizzBuzz(2));

Podríamos hacer algo tonto if n == 1, pero pasaremos a la solución sensata.

public String FizzBuzz(int n) {
    return n.ToString();
}

Bueno. Esto funcionará para todos los números que no sean FizzBuzz. ¿Cuál es la siguiente entrada que obligará a cambiar el código de producción?

Assert.Equal("Fizz", FizzBuzz(3));

public String FizzBuzz(int n) {
    if (n == 3)
        return "Fizz";
    return n.ToString();
}

Y otra vez. Escribe una prueba que aún no pase.

Assert.Equal("Fizz", FizzBuzz(6));

public String FizzBuzz(int n) {
    if (n % 3 == 0)
        return "Fizz";
    return n.ToString();
}

Y ahora hemos cubierto todos los múltiplos de tres (que no son también múltiplos de cinco, lo notaremos y volveremos).

Todavía no hemos escrito una prueba para "Buzz", así que escribamos eso.

Assert.Equal("Buzz", FizzBuzz(5));

public String FizzBuzz(int n) {
    if (n % 3 == 0)
        return "Fizz";
    if (n == 5)
        return "Buzz"
    return n.ToString();
}

Y nuevamente, sabemos que hay otro caso que debemos manejar.

Assert.Equal("Buzz", FizzBuzz(10));

public String FizzBuzz(int n) {
    if (n % 3 == 0)
        return "Fizz";
    if (n % 5 == 0)
        return "Buzz"
    return n.ToString();
}

Y ahora podemos manejar todos los múltiplos de 5 que no son también múltiplos de 3.

Hasta este momento, hemos estado ignorando el paso de refactorización, pero veo algunas duplicaciones. Vamos a limpiar eso ahora.

private bool isDivisibleBy(int divisor, int input) {
    return (input % divisor == 0);
}

public String FizzBuzz(int n) {
    if (isDivisibleBy(3, n))
        return "Fizz";
    if (isDivisibleBy(5, n))
        return "Buzz"
    return n.ToString();
}

Bueno. Ahora hemos eliminado la duplicación y creado una función bien nombrada. ¿Cuál es la próxima prueba que podemos escribir que nos obligará a cambiar el código? Bueno, hemos estado evitando el caso donde el número es divisible entre 3 y 5. Escribámoslo ahora.

Assert.Equal("FizzBuzz", FizzBuzz(15));

public String FizzBuzz(int n) {
    if (isDivisibleBy(3, n) && isDivisibleBy(5, n))
        return "FizzBuzz";
    if (isDivisibleBy(3, n))
        return "Fizz";
    if (isDivisibleBy(5, n))
        return "Buzz"
    return n.ToString();
}

Las pruebas pasan, pero tenemos más duplicaciones. Tenemos opciones, pero voy a aplicar "Extraer variable local" varias veces para que reescribamos en lugar de reescribir.

public String FizzBuzz(int n) {

    var isDivisibleBy3 = isDivisibleBy(3, n);
    var isDivisibleBy5 = isDivisibleBy(5, n);

    if ( isDivisibleBy3 && isDivisibleBy5 )
        return "FizzBuzz";
    if ( isDivisibleBy3 )
        return "Fizz";
    if ( isDivisibleBy5 )
        return "Buzz"
    return n.ToString();
}

Y hemos cubierto todos los aportes razonables, pero ¿qué pasa con los aportes irrazonables ? ¿Qué sucede si pasamos 0 o un negativo? Escribe esos casos de prueba.

public String FizzBuzz(int n) {

    if (n < 1)
        throw new InvalidArgException("n must be >= 1);

    var isDivisibleBy3 = isDivisibleBy(3, n);
    var isDivisibleBy5 = isDivisibleBy(5, n);

    if ( isDivisibleBy3 && isDivisibleBy5 )
        return "FizzBuzz";
    if ( isDivisibleBy3 )
        return "Fizz";
    if ( isDivisibleBy5 )
        return "Buzz"
    return n.ToString();
}

¿Esto ya empieza a parecerse a "código real"? Más importante aún, ¿en qué punto dejó de ser "código irreal" y pasó a ser "real"? Eso es algo para reflexionar ...

Entonces, pude hacer esto simplemente buscando una prueba que sabía que no pasaría en cada paso, pero he tenido mucha práctica. Cuando estoy en el trabajo, las cosas nunca son tan simples y no siempre sé qué prueba forzará un cambio. ¡A veces escribo una prueba y me sorprende ver que ya pasa! Le recomiendo que tenga el hábito de crear una "Lista de prueba" antes de comenzar. Esta lista de prueba debe contener todas las entradas "interesantes" que se te ocurran. Es posible que no los use todos y probablemente agregará casos a medida que avanza, pero esta lista sirve como una hoja de ruta. Mi lista de pruebas para FizzBuzz se vería así.

  • Negativo
  • Cero
  • Uno
  • Dos
  • Tres
  • Cuatro
  • Cinco
  • Seis (múltiplo no trivial de 3)
  • Nueve (3 al cuadrado)
  • Diez (múltiplo no trivial de 5)
  • 15 (múltiplo de 3 y 5)
  • 30 (múltiplo no trivial de 3 y 5)
RubberDuck
fuente
3
Los comentarios no son para discusión extendida; Esta conversación se ha movido al chat .
maple_shaft
47
A menos que esté entendiendo completamente esta respuesta: "Podríamos hacer algo tonto como si n == 1, pero pasaremos a la solución sensata". - Todo fue una tontería. Si sabe de antemano que desea una función que haga <spec>, escriba pruebas para <spec> y omita la parte donde escribe versiones que obviamente fallan en <spec>. Si encuentra un error en <spec>, entonces seguro: escriba primero una prueba para verificar que puede ejercerla antes de la corrección y observe que la prueba pasa después de la corrección. Pero no hay necesidad de fingir todos estos pasos intermedios.
GManNickG
16
Los comentarios que señalan las principales fallas en esta respuesta y TDD en general se han trasladado al chat. Si está considerando usar TDD, lea el 'chat'. Desafortunadamente, los comentarios de "calidad" ahora están ocultos entre una gran cantidad de conversaciones para que los futuros estudiantes puedan leer.
user3791372
2
@GManNickG Creo que el punto es obtener la cantidad correcta de pruebas. Escribir las pruebas de antemano hace que sea fácil pasar por alto qué casos especiales se deben evaluar, lo que lleva a situaciones que no se cubren adecuadamente en las pruebas o esencialmente a la misma situación que se cubre innecesariamente muchas veces en las pruebas. Si puedes hacer eso sin estos pasos intermedios, ¡genial! Sin embargo, no todos pueden hacerlo, es algo que requiere práctica.
hvd
1
Y aquí hay una cita de Kent Beck sobre la refactorización: "Ahora que se ejecuta la prueba, podemos darnos cuenta (como en" hacer real ") la implementación de summary ()". Luego procede a cambiar una constante a una variable. Sentí que esta cita coincidía bastante bien con la pregunta.
Chris Wohlert
46

El código "real" es el código que escribe para que su prueba pase. De verdad . Es así de simple.

Cuando la gente habla de escribir lo mínimo para hacer que la prueba sea verde, eso solo significa que su código real debe seguir el principio YAGNI .

La idea del paso de refactorización es simplemente limpiar lo que ha escrito una vez que esté satisfecho de que cumple con los requisitos.

Siempre que las pruebas que escriba abarquen los requisitos de su producto, una vez que pasen, el código estará completo. Piénselo, si todos sus requisitos comerciales tienen una prueba y todas esas pruebas son ecológicas, ¿qué más hay para escribir? (De acuerdo, en la vida real no tendemos a tener una cobertura de prueba completa, pero la teoría es sólida).

GenericJon
fuente
45
Las pruebas unitarias en realidad no pueden abarcar los requisitos de su producto incluso para requisitos relativamente triviales. En el mejor de los casos, muestrean el espacio de entrada-salida y la idea es que (correctamente) generalice al espacio completo de entrada-salida. Por supuesto, su código podría ser muy grande switchcon un caso para cada prueba unitaria que pasaría todas las pruebas y fallaría para cualquier otra entrada.
Derek Elkins
8
@DerekElkins TDD exige pruebas fallidas. No fallan las pruebas unitarias.
Taemyr
66
@DerekElkins es por eso que no solo escribes pruebas unitarias, ¡y también por qué hay una suposición general de que estás tratando de hacer algo, no solo falsificarlo!
jonrsharpe
36
@jonrsharpe Según esa lógica, nunca escribiría implementaciones triviales. Por ejemplo, en el ejemplo de FizzBuzz en la respuesta de RubberDuck (que solo usa pruebas unitarias), la primera implementación claramente "simplemente lo falsifica". Mi comprensión de la pregunta es exactamente esta dicotomía entre escribir código que sabes que está incompleto y código que realmente crees que implementará el requisito, el "código real". Mi "gran switch" tenía la intención de ser un extremo lógico de "escribir lo mínimo para hacer las pruebas verdes". Veo la pregunta del OP como: ¿en qué parte de TDD está el principio que evita esto switch?
Derek Elkins
2
@GenericJon Eso es un poco demasiado optimista en mi experiencia :) Por un lado, hay personas que disfrutan del trabajo repetitivo sin sentido. Serán más felices con una declaración de cambio gigante que con una "toma de decisiones complicada". Y para perder su trabajo, necesitarían a alguien que los llame a la técnica (y es mejor que tengan una buena evidencia de que realmente está perdiendo las oportunidades / dinero de la compañía), o lo hacen excepcionalmente mal. Después de encargarme del mantenimiento de muchos de estos proyectos, puedo decir que es fácil que un código muy ingenuo dure décadas, siempre y cuando el cliente esté contento (y pague).
Luaan
14

La respuesta corta es que el "código real" es el código que hace pasar la prueba. Si puede hacer que su prueba pase con algo que no sea código real, ¡agregue más pruebas!

Estoy de acuerdo en que muchos tutoriales sobre TDD son simplistas. Eso va en contra de ellos. Una prueba demasiado simple para un método que, por ejemplo, calcula 3 + 8 realmente no tiene más remedio que calcular también 3 + 8 y comparar el resultado. Eso hace que parezca que solo va a duplicar el código, y que las pruebas son un trabajo adicional inútil y propenso a errores.

Cuando sea bueno en las pruebas, eso informará cómo estructura su aplicación y cómo escribe su código. Si tiene problemas para realizar pruebas sensatas y útiles, probablemente debería repensar un poco su diseño. Un sistema bien diseñado es fácil de probar, lo que significa que las pruebas sensatas son fáciles de pensar e implementar.

Cuando escriba sus pruebas primero, mírelas fallar y luego escriba el código que las hace pasar, eso es una disciplina para garantizar que todo su código tenga las pruebas correspondientes. No sigo servilmente esa regla cuando estoy codificando; a menudo escribo pruebas después del hecho. Pero hacer las pruebas primero te ayuda a ser honesto. Con un poco de experiencia, comenzará a notar cuándo se está codificando en una esquina, incluso cuando no está escribiendo primero las pruebas.

Carl Raymond
fuente
66
Personalmente, la prueba que escribiría sería assertEqual(plus(3,8), 11), no assertEqual(plus(3,8), my_test_implementation_of_addition(3,8)). Para casos más complejos, siempre busca una forma de probar el resultado correcto, que no sea calcular dinámicamente el resultado correcto en la prueba y verificar la igualdad.
Steve Jessop
Entonces, para una forma realmente tonta de hacerlo para este ejemplo, puede probar que plus(3,8)ha devuelto el resultado correcto restando 3, restando 8 de eso y verificando el resultado contra 0. Esto es tan obviamente equivalente assertEqual(plus(3,8), 3+8)a ser un es un poco absurdo, pero si el código bajo prueba está creando algo más complicado que solo un número entero, entonces tomar el resultado y verificar que cada parte sea correcta suele ser el enfoque correcto. Alternativamente, algo así comofor (i=0, j=10; i < 10; ++i, ++j) assertEqual(plus(i, 10), j)
Steve Jessop
... ya que eso evita el gran temor, que es que al escribir la prueba cometeremos el mismo error sobre el tema de "cómo agregar 10" que hicimos en el código en vivo. Entonces, la prueba evita cuidadosamente escribir cualquier código que agregue 10 a cualquier cosa, en la prueba que plus()puede agregar 10 a las cosas. Todavía confiamos en los valores de bucle inicial verificados por el programador, por supuesto.
Steve Jessop
44
Solo quiero señalar que incluso si está escribiendo pruebas después del hecho, sigue siendo una buena idea verlas fallar; encuentre alguna parte del código que parezca crucial para lo que sea que esté trabajando, modifíquelo un poco (por ejemplo, reemplace a + con un -, o lo que sea), ejecute las pruebas y vea cómo fallan, deshaga el cambio y vea cómo pasan. Muchas veces he hecho esto, la prueba en realidad no falla, lo que lo hace peor que inútil: no solo no prueba nada, ¡me da una falsa confianza de que algo se está probando!
Warbo
6

A veces, algunos ejemplos sobre TDD pueden ser engañosos. Como otras personas han señalado antes, el código que usted escribe para pasar las pruebas es el código real.

Pero no piense que el código real parece mágico, eso está mal. Necesita una mejor comprensión de lo que desea lograr y luego debe elegir la prueba en consecuencia, comenzando por los casos más fáciles y los casos de esquina.

Por ejemplo, si necesita escribir un lexer, comienza con una cadena vacía, luego con un montón de espacios en blanco, luego un número, luego con un número rodeado de espacios en blanco, luego un número incorrecto, etc. Estas pequeñas transformaciones lo llevarán a el algoritmo correcto, pero no saltas del caso más fácil a un caso altamente complejo elegido tontamente para obtener el código real.

Bob Martin lo explica perfectamente aquí .

Victor Cejudo
fuente
5

La parte del refactorizador se limpia cuando estás cansado y quieres ir a casa.

Cuando esté a punto de agregar una función, la parte del refactorizador es lo que cambia antes de la próxima prueba. Refactoriza el código para dejar espacio para la nueva característica. Hace esto cuando sabe cuál será esa nueva característica. No cuando lo estás imaginando.

Esto puede ser tan simple como cambiar el nombre GreetImplde GreetWorldantes de crear una GreetMomclase (después de la adición de una prueba) para añadir una característica que debe imprimir "Hola mamá".

naranja confitada
fuente
1

Pero el código real aparecería en la etapa de refactorización de la fase TDD. Es decir, el código que debería ser parte de la versión final.

Las pruebas deben ejecutarse cada vez que realice un cambio.

El lema del ciclo de vida de TDD sería: REFACTOR VERDE ROJO

ROJO : Escribe las pruebas

VERDE : Haga un intento honesto de obtener código funcional que pase las pruebas lo más rápido posible: código duplicado, hacks de variables de nombre oscuro del orden más alto, etc.

REFACTOR : Limpia el código, nombra correctamente las variables. SECAR el código.

graeme
fuente
66
Sé lo que estás diciendo sobre la fase "Verde", pero implica que los valores de retorno de cableado para hacer pasar las pruebas podrían ser apropiados. En mi experiencia, "Green" debería ser un intento honesto de hacer que el código de trabajo cumpla con el requisito, puede que no sea perfecto, pero debe ser tan completo y "enviable" como el desarrollador puede administrar en un primer paso. Es probable que la refactorización se haga mejor algún tiempo después, después de que haya desarrollado más y los problemas con el primer pase se vuelvan más aparentes y surjan las oportunidades para SECAR.
mcottle
2
@mcottle: es posible que se sorprenda de cuántas implementaciones de un repositorio get-only pueden ser valores codificados en la base de código. :)
Bryan Boettcher
66
¿Por qué debería escribir código basura y limpiarlo, cuando puedo producir un código de calidad de producción casi tan rápido como puedo escribir? :)
Kaz
1
@Kaz Porque de esta manera te arriesgas a agregar un comportamiento no probado . La única forma de asegurarse de tener una prueba para cada comportamiento deseado es hacer el cambio más simple posible, independientemente de lo horrible que sea. A veces, la siguiente refactorización trae a colación un nuevo enfoque que no pensó de antemano ...
Timothy Truckle
1
@TimothyTruckle ¿Qué pasa si lleva 50 minutos encontrar el cambio más simple posible, pero solo 5 para encontrar el segundo cambio más simple posible? ¿Vamos con el segundo más simple o seguimos buscando el más simple?
Kaz
1

¿Cuándo escribes el código "real" en TDD?

La fase roja es donde escribes el código.

En la fase de refactorización , el objetivo principal es eliminar el código.

En la fase roja , debe hacer todo lo posible para que la prueba pase lo más rápido posible y a cualquier costo . Ignora por completo lo que ha escuchado sobre buenas prácticas de codificación o patrones de diseño similares. Hacer que la prueba sea verde es todo lo que importa.

En la fase de refactorización , limpia el desorden que acaba de hacer. Ahora observa primero si el cambio que acabas de hacer es el que está más arriba en la lista de Prioridad de transformación y si hay alguna duplicación de código que probablemente puedas eliminar aplicando un patrón de diseño.

Finalmente, mejora la legibilidad al renombrar identificadores y extraer números mágicos y / o cadenas literales a constantes.


No es refactor rojo, es refactor rojo-verde. - Rob Kinyon

Gracias por señalar esto.

Entonces es la fase verde donde escribes el código real

En la fase roja , escribe la especificación ejecutable ...

Timothy Truckle
fuente
No es refactor rojo, es refactor rojo-verde. El "rojo" es que lleva su conjunto de pruebas de verde (todas las pruebas pasan) a rojo (una prueba falla). El "verde" es donde descuidadamente lleva su conjunto de pruebas de rojo (una prueba falla) a verde (todas las pruebas pasan). El "refactor" es donde tomas tu código y lo haces bonito mientras mantienes todas las pruebas aprobadas.
Rob Kinyon
1

Estás escribiendo Código Real todo el tiempo.

En cada paso, está escribiendo un código para satisfacer las condiciones que su código satisfará para las futuras llamadas de su código (que podría ser usted o no ...).

Cree que no está escribiendo código útil ( real ), porque en un momento podría refactorizarlo.

La refactorización de código es el proceso de reestructurar el código de computadora existente, cambiar la factorización, sin cambiar su comportamiento externo.

Lo que esto significa es que a pesar de que está cambiando el código, las condiciones que el código cumple no se modifican. Y las verificaciones ( pruebas ) que implementó para verificar su código ya están allí para verificar si sus modificaciones cambiaron algo. Entonces, el código que escribió todo el tiempo está allí, solo que de una manera diferente.

Otra razón por la que podría pensar que no es un código real, es que está haciendo ejemplos en los que el programa final ya puede ser visto por usted. Esto es muy bueno, ya que muestra que tienes conocimiento sobre el dominio en el que estás programando.
Pero muchas veces los programadores están en un dominio que es nuevo , desconocido para ellos. No saben cuál será el resultado final y TDD es una técnica para escribir programas paso a paso, documentando nuestro conocimiento sobre cómo debería funcionar este sistema y verificando que nuestro código funcione de esa manera.

Cuando leí The Book (*) en TDD, para mí, la característica más importante que se destacó fue: la lista TODO. Me demostró que, TDD también es una técnica para ayudar a los desarrolladores a centrarse en una cosa a la vez. Entonces, esta también es una respuesta a su pregunta sobre ¿Cuánto código real escribir ? Yo diría suficiente código para enfocarme en 1 cosa a la vez.

(*) "Test Driven Development: By Example" de Kent Beck

Robert Andrzejuk
fuente
2
"Test Driven Development: By Example" por Kent Beck
Robert Andrzejuk
1

No estás escribiendo código para hacer que tus pruebas fallen.

Escribes tus pruebas para definir cómo debería ser el éxito, que inicialmente debería fallar porque aún no has escrito el código que pasará.

El objetivo principal de escribir pruebas inicialmente fallidas es hacer dos cosas:

  1. Cubra todos los casos: todos los casos nominales, todos los casos extremos, etc.
  2. Valida tus pruebas. Si solo los ve pasar, ¿cómo puede estar seguro de que informarán de manera confiable un fallo cuando ocurra uno?

El punto detrás de red-green-refactor es que escribir primero las pruebas correctas le da la confianza de saber que el código que escribió para aprobar las pruebas es correcto y le permite refactorizar con la confianza de que sus pruebas le informarán tan pronto algo se rompe, por lo que puede volver inmediatamente y arreglarlo.

En mi propia experiencia (C # / .NET), la prueba pura primero es un poco un ideal inalcanzable, porque no puede compilar una llamada a un método que aún no existe. Por lo tanto, "probar primero" se trata realmente de codificar las interfaces y las implementaciones de stubbing primero, luego escribir las pruebas contra los stubs (que inicialmente fallarán) hasta que los stubs estén debidamente desarrollados. Nunca escribo "código defectuoso", solo construyo desde trozos.

Zenilogix
fuente
0

Creo que puede confundirse entre las pruebas unitarias y las pruebas de integración. Creo que también puede haber pruebas de aceptación, pero eso depende de su proceso.

Una vez que ha probado todas las pequeñas "unidades", entonces las prueba todas ensambladas o "integradas". Eso suele ser un programa completo o una biblioteca.

En el código que he escrito, la integración prueba una biblioteca con varios programas de prueba que leen datos y los envían a la biblioteca, luego verifican los resultados. Luego lo hago con hilos. Luego lo hago con hilos y tenedor () en el medio. Luego lo ejecuto y mato -9 después de 2 segundos, luego lo inicio y verifico su modo de recuperación. Lo fumo. Lo torturo de muchas maneras.

Todo eso también está probando, pero no tengo una pantalla roja / verde para los resultados. O tiene éxito, o busco algunos miles de líneas de código de error para averiguar por qué.

Ahí es donde se prueba el "código real".

Y solo pensé en esto, pero tal vez no sabes cuándo se supone que debes terminar de escribir pruebas unitarias. Ha terminado de escribir pruebas unitarias cuando sus pruebas ejercitan todo lo que especificó que debería hacer. A veces, puede perder el rastro de eso entre todos los casos de manejo de errores y casos límite, por lo que es posible que desee hacer un buen grupo de pruebas de pruebas de ruta feliz que simplemente pasen directamente por las especificaciones.

Zan Lynx
fuente
(es = posesivo, es = "es" o "tiene". Vea, por ejemplo, cómo usarlo y es .)
Peter Mortensen
-6

En respuesta al título de la pregunta: "¿Cuándo escribes el código" real "en TDD?", La respuesta es: "casi nunca" o "muy lentamente".

Suenas como un estudiante, entonces responderé como si estuvieras aconsejando a un estudiante.

Aprenderá muchas 'teorías' y 'técnicas' de codificación. Son excelentes para pasar el tiempo en cursos de estudiantes caros, pero de muy poco beneficio para usted que no podría leer en un libro en la mitad del tiempo.

El trabajo de un codificador es únicamente producir código. Código que funciona muy bien. Es por eso que usted, el codificador, planifica el código en su mente, en papel, en una aplicación adecuada, etc., y planea solucionar posibles fallas / agujeros de antemano pensando lógicamente y lateralmente antes de la codificación.

Pero necesita saber cómo romper su aplicación para poder diseñar un código decente. Por ejemplo, si no conocía Little Bobby Table (xkcd 327), entonces probablemente no estaría desinfectando sus entradas antes de trabajar con la base de datos, por lo que no podría proteger sus datos en torno a ese concepto.

TDD es solo un flujo de trabajo diseñado para minimizar los errores en su código al crear las pruebas de lo que podría salir mal antes de codificar su aplicación porque la codificación puede ser exponencialmente difícil cuanto más código introduzca y olvide los errores que alguna vez pensó. Una vez que crees que has terminado tu aplicación, ejecutas las pruebas y el boom, con suerte se detectan errores con tus pruebas.

TDD no es, como algunas personas creen, escribir una prueba, pasarla con un código mínimo, escribir otra prueba, pasarla con un código mínimo, etc. En cambio, es una forma de ayudarlo a codificar con confianza. Este ideal de código de refactorización continua para que funcione con las pruebas es idiota, pero es un buen concepto entre los estudiantes porque los hace sentir bien cuando agregan una nueva función y todavía están aprendiendo a codificar ...

No caiga en esta trampa y vea su función de codificación de lo que es: el trabajo de un codificador es únicamente producir código. Código que funciona muy bien. Ahora, recuerde que estará en el reloj como codificador profesional, y a su cliente no le importará si escribió 100,000 afirmaciones, o 0. Solo quieren un código que funcione. Muy bien, de hecho.

usuario3791372
fuente
3
Ni siquiera estoy cerca de un estudiante, pero leo e intento aplicar buenas técnicas y ser profesional. Entonces, en ese sentido, soy un "estudiante". Solo hago preguntas muy básicas porque así soy yo. Me gusta saber exactamente por qué estoy haciendo lo que estoy haciendo. Lo importante del asunto. Si no entiendo eso, no me gusta y empiezo a hacer preguntas. Necesito saber por qué, si voy a usarlo. TDD parece intuitivamente bueno en algunos aspectos, como saber lo que necesita crear y pensar las cosas, pero la implementación fue difícil de entender. Creo que ahora tengo una mejor comprensión.
johnny
44
Esas son las reglas de TDD. Eres libre de escribir el código como quieras, pero si no sigues esas tres reglas, no estás haciendo TDD.
Sean Burton
2
¿"Reglas" de una persona? TDD es una sugerencia para ayudarlo a codificar, no una religión. Es triste ver a tanta gente adherirse a una idea de manera tan anal. Incluso el origen de TDD es controvertido.
user3791372
2
@ user3791372 TDD es un proceso muy estricto y claramente definido. Incluso si muchos piensan que eso solo significa "Haz algunas pruebas cuando estés programando", no lo es. Intentemos no mezclar términos aquí, esta pregunta es sobre el proceso TDD, no sobre pruebas generales.
Alex