Quería aprender a usar el enfoque TDD y tenía un proyecto en el que quería trabajar durante un tiempo. No era un proyecto grande, así que pensé que sería un buen candidato para TDD. Sin embargo, siento que algo salió mal. Déjame dar un ejemplo:
En un nivel superior, mi proyecto es un complemento para Microsoft OneNote que me permitirá rastrear y administrar proyectos más fácilmente. Ahora, también quería mantener la lógica de negocios para esto tan desacoplada de OneNote como sea posible en caso de que decidiera construir mi propio almacenamiento personalizado y algún día.
Primero comencé con una prueba básica de aceptación de palabras simples para describir lo que quería que hiciera mi primera función. Se ve más o menos así (para simplificarlo):
- Clics del usuario crear proyecto
- Tipos de usuario en el título del proyecto
- Verifique que el proyecto se haya creado correctamente
Saltando sobre las cosas de la interfaz de usuario y algunos planes intermedios, llego a mi primera prueba de unidad:
[TestMethod]
public void CreateProject_BasicParameters_ProjectIsValid()
{
var testController = new Controller();
Project newProject = testController(A.Dummy<String>());
Assert.IsNotNull(newProject);
}
Hasta aquí todo bien. Rojo, verde, refactor, etc. Bien, ahora realmente necesita guardar cosas. Cortando algunos pasos aquí termino con esto.
[TestMethod]
public void CreateProject_BasicParameters_ProjectMatchesExpected()
{
var fakeDataStore = A.Fake<IDataStore>();
var testController = new Controller(fakeDataStore);
String expectedTitle = fixture.Create<String>("Title");
Project newProject = testController(expectedTitle);
Assert.AreEqual(expectedTitle, newProject.Title);
}
Todavía me siento bien en este punto. Todavía no tengo un almacén de datos concreto, pero creé la interfaz como esperaba.
Voy a omitir algunos pasos aquí porque esta publicación es lo suficientemente larga, pero seguí procesos similares y finalmente llego a esta prueba para mi almacén de datos:
[TestMethod]
public void SaveNewProject_BasicParameters_RequestsNewPage()
{
/* snip init code */
testDataStore.SaveNewProject(A.Dummy<IProject>());
A.CallTo(() => oneNoteInterop.SavePage()).MustHaveHappened();
}
Esto fue bueno hasta que intenté implementarlo:
public String SaveNewProject(IProject project)
{
Page projectPage = oneNoteInterop.CreatePage(...);
}
Y ahí está el problema justo donde está el "...". Ahora me doy cuenta en ESTE punto que CreatePage requiere una ID de sección. No me di cuenta de esto cuando estaba pensando en el nivel del controlador porque solo me preocupaba probar los bits relevantes para el controlador. Sin embargo, hasta aquí ahora me doy cuenta de que tengo que pedirle al usuario una ubicación para almacenar el proyecto. Ahora tengo que agregar un ID de ubicación al almacén de datos, luego agregar uno al proyecto, luego agregar uno al controlador y agregarlo a TODAS las pruebas que ya están escritas para todas esas cosas. Se ha vuelto tedioso muy rápidamente y no puedo evitar sentir que habría captado esto más rápido si esbozara el diseño con anticipación en lugar de dejar que se diseñara durante el proceso TDD.
¿Puede alguien explicarme si he hecho algo mal en este proceso? ¿Hay alguna forma de evitar este tipo de refactorización? ¿O es esto común? Si es común, ¿hay alguna forma de hacerlo más indoloro?
¡Gracias a todos!
fuente
Respuestas:
Si bien TDD se promociona (correctamente) como una forma de diseñar y hacer crecer su software, sigue siendo una buena idea pensar de antemano en el diseño y la arquitectura. En mi opinión, "esbozar el diseño con anticipación" es un juego justo. Sin embargo, a menudo esto será a un nivel más alto que las decisiones de diseño a las que se lo llevará a través de TDD.
También es cierto que cuando las cosas cambian, generalmente tendrá que actualizar las pruebas. No hay forma de eliminar esto por completo, pero hay algunas cosas que puede hacer para que sus pruebas sean menos frágiles y minimizar el dolor.
En la medida de lo posible, mantenga los detalles de implementación fuera de sus pruebas. Esto significa probar solo a través de métodos públicos y, cuando sea posible, favorecer la verificación basada en el estado sobre la interacción . En otras palabras, si prueba el resultado de algo en lugar de los pasos para llegar allí, sus pruebas deberían ser menos frágiles.
Minimice la duplicación en su código de prueba, tal como lo haría en el código de producción. Esta publicación es una buena referencia. En su ejemplo, parece que fue doloroso agregar la
ID
propiedad a su constructor porque invocó al constructor directamente en varias pruebas diferentes. En su lugar, intente extraer la creación del objeto a un método o inicializarlo una vez para cada prueba en un método de inicialización de prueba.fuente
Tal vez tal vez no
Por un lado, TDD funcionó bien, brindándole pruebas automatizadas a medida que desarrolló la funcionalidad e inmediatamente interrumpiendo cuando tuvo que cambiar la interfaz.
Por otro lado, quizás si hubiera comenzado con la función de alto nivel (SaveProject) en lugar de una función de nivel inferior (CreateProject), habría notado la falta de parámetros antes.
Por otra parte, tal vez no lo hubieras hecho. Es un experimento irrepetible.
Pero si está buscando una lección para la próxima vez: comience desde arriba. Y piense en el diseño tanto como quiera primero.
fuente
https://frontendmasters.com/courses/angularjs-and-code-testability/ Desde aproximadamente las 2:22:00 hasta el final (aproximadamente 1 hora). Lamento que el video no sea gratuito, pero no he encontrado uno gratuito que lo explique tan bien.
Una de las mejores presentaciones de escribir código comprobable es en esta lección. Es una clase AngularJS, pero la parte de la prueba está en torno al código java, principalmente porque lo que está hablando no tiene nada que ver con el lenguaje, y todo lo que tiene que ver con escribir un buen código comprobable en primer lugar.
La magia está en escribir código comprobable, en lugar de escribir pruebas de código. No se trata de escribir código que pretende ser un usuario.
También pasa algún tiempo escribiendo la especificación en forma de afirmaciones de prueba.
fuente