En TDD, ¿debo escribir primero Prueba o Interfaz primero?

23

Estoy aprendiendo TDD usando c #, por lo que sé, la prueba debería impulsar el desarrollo , es decir, primero escribir una prueba fallida después de escribir el código mínimo para pasar la prueba y luego refactorizar.

Pero también se dice que " Programa a la interfaz, no a la implementación ", así que primero escribe una interfaz . Aquí es donde comienza mi confusión, si estoy escribiendo Interface primero, entonces está violando dos cosas

  1. El código que está escrito para la interfaz no está controlado por la prueba .

  2. No es lo mínimo, obviamente, puedo escribirlo con una clase simple.

¿Debo comenzar escribiendo pruebas para la interfaz también? sin ninguna implementación, ¿qué voy a probar?

Si esta pregunta suena tonta, perdón por eso, pero estoy completamente confundido. Puede ser que estoy tomando las cosas demasiado literalmente.

k4vin
fuente
8
"Programar a una interfaz" significa separar lo que necesita de un código de cómo se hace. No significa literalmente usar un interfacepara todo. A classtambién proporciona una interfaz, porque puede ocultar detalles de implementación en privatevariables.
Doval
@Doval, sí, no necesitas una interfaz para todo, solo lo que se llama a contract. Esto podría ser en forma de una clase abstracta, por ejemplo, aunque no debería ser una clase / método virtual porque no debería ser capaz de crear una instancia.
trysis
2
TDD dice, "escribe una prueba que falla". Algunos TDD estrictos dicen que cuenta como un "error" si no puede compilar la prueba porque el tipo de datos en el que opera aún no se ha declarado.
Solomon Slow

Respuestas:

29

Su primera infracción ("El código que se escribe para la interfaz no se controla mediante prueba") no es válido. Usemos un ejemplo trivial. Supongamos que está escribiendo una clase de calculadora y está escribiendo una operación de suma. ¿Qué prueba podrías escribir?

public class CalculatorTest {
    @Test
    public void testAddTwoIntegers() {
        Calculator calc = new Calculator();
        int result = calc.add(2, 2)
        Assert.assertEquals(4, result);
    }
}

Su prueba acaba de definir la interfaz. Es el addmétodo, ¿ves? addtoma dos argumentos y devuelve su suma. Posteriormente, puede determinar que necesita varias calculadoras y extraer una interfaz Java (en este caso) en ese momento. Sus pruebas no deberían cambiar entonces, ya que probó la interfaz pública de esa clase.

En un nivel más teórico, las pruebas son la especificación ejecutable de un sistema. Las interfaces con un sistema deben ser conducidas por los usuarios de ese sistema, y ​​las pruebas son el primer método que tiene para definir interacciones.

No creo que pueda separar el diseño de la interfaz del diseño de prueba. Definir interacciones y diseñar pruebas para ellas es la misma operación mental: cuando envío esta información a una interfaz, espero un cierto resultado. Cuando algo está mal con mi entrada, espero este error. Puede hacer este trabajo de diseño en papel y luego escribir sus pruebas a partir de eso, o puede hacerlos al mismo tiempo, realmente no importa.

Michael K
fuente
2
+1 " No creo que puedas separar el diseño de la interfaz del diseño de prueba " debería estar en negrita, en mi humilde opinión :)
Binary Worrier
Es aún más fácil de mostrar, si desea probar más de una implementación de una funcionalidad, dice una importación XML y una importación CSV, puede probarlas con exactamente el mismo método desde la misma interfaz, aunque la implementación cambiará. Además, las pruebas implican a menudo algunas burlas, y para esta interfaz son necesarias.
Walfrat
Usted dice que la prueba no necesita cambiar, pero ¿ new Calculator()es correcta la implementación? Si se necesita una nueva implementación, ¿tal vez harías un MultiplicationCalculator y tendrías que cambiar la prueba new AdditionCalculator()para que todavía se apruebe? O me estoy perdiendo algo ?
Steve Chamaillard
3
@SteveChamaillard Claro, si su diseño hace que cambie el nombre de la clase de Calculadora a AdditionCalculator, la prueba tendría que cambiar para que coincida. Por supuesto, al hacer TDD, lo que realmente sucedería es que primero cambiaría la prueba, y el cambio de clase seguiría para aprobar la prueba.
Eric King
5

¿Qué estamos haciendo cuando escribimos un interface? ¿Estamos escribiendo código o estamos diseñando?

No soy fanático de la noción de Test Driven Design, pero me encanta Test Driven Development . Personalmente, obtuve mis mejores resultados cuando diseño la clase por adelantado al diseñar la interfaz antes de escribir una prueba. No cuento la interfaz como código. La interfaz es un diseño que implementaré usando TDD. Es probable que cambie y evolucione a medida que trabajo, pero es mi hoja de ruta (junto con mi lista de pruebas).

Me detendré antes de comenzar a despotricar, pero espero que sea una forma útil de pensarlo.

RubberDuck
fuente
4

En TDD, ¿debo escribir primero Prueba o Interfaz primero?

Todo depende de qué tan ortodoxo / religioso quieras hacer TDD .

Estoy aprendiendo TDD

Como está aprendiendo, debe experimentar para obtener un flujo de trabajo personal, que funcione para usted.

  1. Si desea hacerlo de acuerdo con los libros , primero escribe una prueba, que obviamente fallará, porque está comenzando sin ningún código. Luego escribes un código para pasar la prueba. Si se hace eso, puede refactorizar el código existente, ya que tiene una prueba que proporciona algún tipo de red de seguridad para las refactorizaciones. Decidir usar una interfaz es algún tipo de refactorización.

  2. Además de TDD o no: la pregunta, si usar una interfaz o no, no es interesante en primer lugar. Por supuesto, si está seguro, tiene un comportamiento diferente que desea difundir entre varios objetos, tiene sentido pensar en usar una interfaz: por ejemplo, si tiene algún tipo de salida a diferentes destinos, tiene sentido implementarlo a través de un escritor de interfaz y tiene diferentes clases para la salida ( FileWriter , Printer , etc.). Aunque es un dicho común escribir en una interfaz , eso no significa: usar una interfaz para todo . A veces es un nivel de indirección a mucho. Por cierto. Lo mismo ocurre con los servicios. Pero ese es un tema diferente.

  3. Por otro lado, podría desarrollar pruebas conducidas de otra manera: diseñe su código para la comprobabilidad. Lo que significa, que escriben el código, que es fácil de prueba - aunque se escriben las pruebas después . No importa si escribe pruebas de antemano o después, siempre y cuando lo haga de todos modos.

Thomas Junk
fuente
55
No puedo estar de acuerdo con el último punto "No importa si escribes exámenes de antemano o después, siempre y cuando lo hagas". Si luego escribe la prueba, no puede estar seguro de si la prueba está probando lo correcto.
k4vin
44
Como dije ... Depende de lo ortodoxo que seas ...
Thomas Junk
2

TDD o BDD significaría primero hacer sus interfaces de dominio y luego escribir pruebas contra ellas según mi interpretación. La implementación de una interfaz tiene un comportamiento esperado.

todavía es una prueba antes del código porque una interfaz no contiene una lógica comprobable, es la estructura contra la que escribe una prueba.

Lo haría de la siguiente manera

  1. Escriba el comportamiento semi formal (dado: cuándo: entonces :)

  2. Escribir la interfaz (al método de encapsulación del comportamiento del host)

  3. Escriba la prueba que identifica (ingrese el dado, llame al cuándo, pruebe el entonces)

  4. Escribir / alterar el concreto (clase que implementa la interfaz) para pasar la prueba

usuario3721330
fuente
0

Nunca escriba pruebas antes de haber diseñado las interfaces. Cuando esté pensando en qué tipos de pruebas escribir (diseño de prueba), no debería también diseñar (diseñar) su aplicación simultáneamente. No pienses en dos cosas al mismo tiempo. ¿Has oído hablar de la separación de las preocupaciones? Se aplica no solo a la estructura física de su código sino también a su proceso de pensamiento.

Decida cómo se debe diseñar primero su aplicación. Esto significa que diseña tus interfaces y las relaciones entre estas interfaces. Hasta que haya hecho esto, no debe comenzar a pensar en las pruebas. Una vez que sepa cuáles son sus interfaces, puede crearlas primero y luego escribir pruebas contra ellas o escribir pruebas primero y luego crearlas. En este último caso, obviamente no podrá compilar las pruebas. No veo ningún daño ni ninguna violación de la filosofía TDD al crear las interfaces antes de las pruebas.

Nissim Levy
fuente
el razonamiento en la respuesta superior parece más convincente: "No creo que se pueda separar el diseño de la interfaz del diseño de la prueba. Definir interacciones y diseñar pruebas para ellas es la misma operación mental: cuando envío esta información a una interfaz, espero un cierto resultado . Cuando algo está mal con mi entrada, espero este error ... "
mosquito
@gnat Creo que Nissam aquí se refiere a la interfacepalabra clave C # , no al término general "interfaz".
RubberDuck
@RubberDuck También lo creo y creo que este es un enfoque pobre. "Hasta que hayas hecho esto, no deberías comenzar a pensar en las pruebas ..." Como escribí, el razonamiento en la respuesta principal parece más convincente, tanto en el sentido general de la interfaz como en el sentido de la palabra clave concreta
gnat el
Bastante justo @gnat que no estaba claro en tu comentario. Personalmente, estoy de acuerdo con Nissam aquí. Me parece que impide que las personas usen TDD como una excusa para no diseñar en absoluto. YMMV.
RubberDuck
-2

Está bien escribir la interfaz / código / prueba al mismo tiempo siempre que su incorporación al proyecto sea atómica.

A menos que su jefe sea religioso sobre TDD, en cuyo caso probablemente tenga que escribir una interfaz vacía -> prueba -> código mínimo (paso sin sentido) -> más pruebas -> más código sin sentido -> más pruebas -> finalmente escriba el código real - > hecho.

alternativa
fuente