java - pruebas - tdd ejemplo
Coma, Dormir y Respirar Pruebas unitarias/TDD/BDD (12)
Escribo pruebas unitarias mientras escribo API y funcionalidades principales. Pero quiero ser el fanático genial que come, duerme y respira TDD y BDD. ¿Cuál es la mejor manera de comenzar con TDD / BDD de la manera correcta? ¿Cualesquiera libros, recursos, marcos, mejores prácticas?
Mi entorno es el backend de Java con interfaz Grails, integrado con varios servicios web externos y bases de datos.
"PD: Mi manager dice que la única forma de obtener una promoción es si puedo llevar al equipo a TDD / BDD".
La única manera realista de hacer que un equipo haga algo (sin matarlo en el proceso) es demostrarles claramente que les beneficiará cambiar sus hábitos. En otras palabras, escribe el código. Montones de código Toneladas de código. Y luego, cuando llega el correo electrónico crucial que altera radicalmente la especificación, demuéstreles que puede cambiar su código fácilmente con refactorización y lo que es peor porque ya estaba preparado para eso con sus pruebas en su lugar. El bar era verde, hack hackear hack, RED BAR !!!!, hackear hackear hack, barra verde, ir a casa.
Lea el libro de Kent Becks sobre el diseño impulsado por pruebas. Comience con las pruebas y luego haga el código. ¡Obtenga un servidor de compilación en ejecución que EJECUTE LAS PRUEBAS! No necesita tenerlo para todo el equipo; hágalo por usted mismo y muéstreles que ayuda.
La predicación solo molesta a los nativos :)
Encuentre a alguien que haya estado haciendo TDD / BDD y empareje el programa con ellos.
Hace un año, tenía poca idea de cómo hacer TDD (pero realmente quería (qué frustrante)) y nunca había escuchado sobre BDD ... ahora hago ambas compulsivamente. He estado en un entorno de desarrollo .Net, no en Java, pero incluso reemplacé el botón "F5 - Ejecutar" con una macro para ejecutar Cucumber (BDD) o MBUnit (TDD) dependiendo de si se trata de una característica / escenario o especificación. Sin depurador si es posible. $ 1 en el jar si usa el depurador (JOKING (tipo de)).
El proceso es muy impresionante. El marco que estamos usando adicionalmente es el Oracle que he tenido la bendición de encontrar, y la absorción de información de, y el marco que él / nosotros usamos es MavenThought.
Todo comienza con BDD. Nuestro BDD es un pepino recto de pepita de rubí.
Característica:
Escenario: .... Dado que hago bla ...
Cuando hago otra cosa ... Entonces suceden cosas maravillosas ...
Escenario: ...
Y eso no es una prueba unitaria, sino que maneja la función, escenario por escenario y, a su vez, las especificaciones de la unidad (prueba). Así que comienzas en un escenario, y con cada paso necesitas completar en el escenario que maneja tu TDD .
Y el TDD que hemos estado usando es una especie de BDD de alguna manera, porque observamos los comportamientos que el SUT (Sistema Bajo Prueba) requiere y se especifica un comportamiento por especificación (archivo de "prueba" de clase).
Ejemplo:
Aquí está la especificación de un comportamiento: cuando se crea el sistema bajo prueba.
Hay una especificación más (C # When_blah_happens class file) para otro comportamiento cuando una propiedad cambia, pero eso se separa en un archivo separado.
using MavenThought.Commons.Testing;
using SharpTestsEx;
namespace Price.Displacement.Module.Designer.Tests.Model.Observers
{
/// <summary>
/// Specification when diffuser observer is created
/// </summary>
[ConstructorSpecification]
public class When_diffuser_observer_is_created
: DiffuserObserverSpecification
{
/// <summary>
/// Checks the diffuser injection
/// </summary>
[It]
public void Should_return_the_injected_diffuser()
{
Sut.Diffuser.Should().Be.SameInstanceAs(this.ConcreteDiffuser);
}
}
}
Este es probablemente el comportamiento más simple para un SUT, porque en este caso cuando se crea, la propiedad Difusor debe ser la misma que el difusor inyectado. Tuve que usar un difusor de concreto en lugar de un simulacro porque en este caso el difusor es un objeto núcleo / dominio y no tiene notificación de propiedad para la interfaz. El 95% de las veces nos referimos a todas nuestras dependencias como Dep (), en lugar de referirnos a algo real.
A menudo tenemos más de uno [It] Should_do_xyz (), y algunas veces un poco de configuración como quizás hasta 10 líneas de stubbing. Este es solo un ejemplo muy simple sin GivenThat () o AndGivenThat AfterCreated () en esa especificación.
Para la configuración de cada especificación, en general, solo necesitamos anular un par de métodos de la especificación:
GivenThat () ==> esto sucede antes de que se cree el SUT.
CreatSut () ==> Automáticamente simulamos la creación del sut con StructureMap y el 90% de las veces nunca necesitamos anular esto, pero si usted es un constructor que inyecta un concreto, debe anularlo.
AndGivenThatAfterCreated () => esto sucede después de que se crea el SUT.
WhenIRun () => a menos que sea una [ConstructorSpecification] usamos esto para ejecutar UNA línea de código que es el comportamiento que estamos especificando para el SUT
Además, si hay un comportamiento común de dos o más especificaciones del mismo SUT, lo trasladamos a la especificación de base.
Todo lo que tengo que hacer para ejecutar la Especificación es resaltar su nombre, ejemplo "When_diffuser_observer_is_created" y presionar F5, porque recuerde, para mí F5 ejecuta una tarea de Rake o prueba: feature [tag] if Cucumber, o test: class [SUT]. Tiene sentido para mí porque cada vez que ejecuta el depurador está a un tiro de distancia, no se crea código (ah, y cuesta $ 1 (bromeando)).
Esta es una manera muy, muy limpia de especificar el comportamiento con TDD y tener SUT realmente simples y especificaciones simples. Si tratas de ser un codificador de vaqueros y escribes un SUT con dependencias duras, etc., sentirás el dolor de tratar de hacer TDD y te cansarás / te rendirás, o morderás la bala y lo harás bien.
Y aquí está el SUT real. Tenemos un poco de fantasía y usamos PostSharp para agregar notificación de propiedad modificada en el Difusor, por lo tanto, el Post.Cast <>. Y nuevamente, es por eso que inyecté un Concreto en lugar de Simulacro. De todos modos, como puede ver, el comportamiento perdido que se define en otra especificación es cuando algo cambia en el difusor.
using System.ComponentModel;
using MavenThought.Commons.Events;
using PostSharp;
using Price.Displacement.Core.Products;
using Price.Displacement.Domain;
namespace Price.Displacement.Desktop.Module.Designer.Model.Observers
{
/// <summary>
/// Implementation of current observer for the selected product
/// </summary>
public class DiffuserObserver : AbstractNotifyPropertyChanged, IDiffuserObserver
{
/// <summary>
/// gets the diffuser
/// </summary>
public IDiffuser Diffuser { get; private set; }
/// <summary>
/// Initialize with a diffuser
/// </summary>
/// <param name="diffuser">The diffuser to observe</param>
public void Initialize(IDiffuser diffuser)
{
this.Diffuser = diffuser;
this.NotifyInterface().PropertyChanged += (x, e) => this.OnPropertyChanged(e.PropertyName);
}
/// <summary>
/// Gets the notify interface to use
/// </summary>
/// <returns>The instance of notify property changed interface</returns>
protected INotifyPropertyChanged NotifyInterface()
{
return Post.Cast<Diffuser, INotifyPropertyChanged>((Diffuser)Diffuser);
}
}
}
En conclusión, este estilo de desarrollo de BDD / TDD es fantástico. Me tomó un año pero soy un converso total como una forma de vida. No habría aprendido esto por mi cuenta. Recogí todo de The Oracle http://orthocoders.com/ .
Pastilla roja o azul, la elección es tuya.
He estado haciendo TDD durante un par de años, pero últimamente he comenzado a buscar más en la forma BDD de impulsar mi diseño y desarrollo. Los recursos que me ayudaron a comenzar BDD fueron primordiales y más importantes para el blog de Dan North (el "fundador" de BDD). Eche un vistazo a Introducción a BDD . También hay una Wiki de BDD "oficial" en behaviour-driven.org con un buen post que vale la pena leer.
Lo único que encontré realmente difícil cuando comencé con BDD (y aún me resulta un poco difícil) es cómo formular esos escenarios para que sean adecuados para BDD. Scott Bellware es un hombre bien capacitado en BDD (o Context-Spesification como le gusta acuñarlo) y su artículo Behavior-Driven Development en Code Magazine me ayudó mucho a comprender la manera de pensar y formular historias de usuario de BDD.
También recomendaría el diseño basado en comportamiento de TekPub screencast con Specflow por Rob Conery. Una gran introducción a BDD y a una herramienta (SpecFlow) muy adecuada para hacer BDD en C #.
En cuanto a los recursos de TDD, ya hay muchas buenas recomendaciones aquí. Pero solo quiero señalar un par de libros que realmente puedo recomendar;
- Trabajando Efectivamente con Legacy Code por Michael Feathers; Una lectura imprescindible si está trabajando con el código heredado (¿no es así?) Y desea probarlo
- El arte de las pruebas unitarias: con ejemplos en .Net de Roy Osherove; Si eres nuevo en pruebas unitarias, este es el libro para que comiences
- http://www.amazon.com/Test-Driven-Development-Kent-Beck/dp/0321146530 por Kent Beck; Si está aprendiendo TDD, ¿por qué no obtenerlo de la fuente misma? Un buen libro: fácil de leer, buen humor y buenos pensamientos.
- http://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882 por Robert C. Martin; Si Kent Beck describe el cómo de TDD, el tío Bob describe los por qué .
Las métricas son, en mi humilde opinión, la mejor manera de ir de aquí para allá. Mantenga un registro de qué tan bien está cubierto su código, mantenga deltas de complejidad del código para cada compromiso, use corredores de prueba que miren su código para ver los cambios y repita constantemente las pruebas correspondientes. Nunca permita que las longitudes de prueba superen unas pocas líneas, para que todas sus herramientas funcionen bien. Y lo recomendaría una vez al mes, tómese un día libre para ejecutar su código a través de un probador de mutaciones. Ese día debería estar dedicado a escribir pruebas solamente. Todas estas cosas te traerán dolor si aún no estás haciendo un buen TDD. Aprende del dolor, y en un momento, lo estarás haciendo bien.
Y nunca pierda de vista para qué son las pruebas: describir el comportamiento deseado. Ellos son su especificación ejecutable. (Esta es también la razón por la que me gusta Cucumber , ahora puedes obtener tu PHB para escribir tus pruebas para ti. Bueno, tal vez no tan bueno, ¡pero está cerca!)
Las mejores prácticas en mi humilde opinión: hacer lo que es práctico y no solo porque es un proceso. No olvide cuál es el objetivo de escribir aplicaciones, y en el mundo de los negocios, no es escribir pruebas. No me malinterpreten, tienen su lugar, pero ese no debería ser el objetivo.
No me gusta cuando la gente dice "La práctica X nunca es mala, si no funciona, no lo estás haciendo bien". Lo siento, tiene la misma sensación que cualquier otro dogma religioso demasiado celoso. No lo compro.
Estoy de acuerdo con aquellos que dicen que la mejor solución que su tiempo y dinero pueden pagar es el objetivo.
Cualquiera que objete a TDD no debería ser acusado automáticamente de ignorar la calidad. ("Entonces, ¿cuándo dejó de golpear a su esposa?"). El hecho es que el software tiene errores y el costo de eliminarlos debe sopesarse contra el beneficio.
Lo mismo es cierto en la fabricación. Las tolerancias en las dimensiones y los acabados de las superficies no son todas iguales, porque a veces no se justifica una tolerancia cercana y un acabado de espejo.
Sí, escribo pruebas unitarias, aunque no a menudo antes de escribir la clase. He visto el efecto de las pruebas en el diseño. Mido y miro la cobertura del código. Si encuentro que mi cobertura no es aceptable, escribo más pruebas. Entiendo el beneficio de una red de seguridad de pruebas unitarias para la refactorización. Sigo esas prácticas incluso cuando trabajo solo, porque he experimentado los beneficios de primera mano. Lo entiendo.
Pero miraba con recelo a cualquier compañero de equipo que comenzara a molestarme acerca de "comer, dormir y probar unidades de respiración y TDD".
Mi gerente dice que la única forma de obtener una promoción es si puedo llevar al equipo a TDD / BDD.
¿Alguna vez pensaste que tal vez esto te hace sonar como una mamada? ¿Has descubierto que tus regaños han alienado al resto de tu equipo?
Esta respuesta podría perder algunos puntos de reputación, pero tenía que decirse.
Creo que un mejor enfoque sería practicarlo usted mismo y dejar que otros vean el beneficio. Predicar con el ejemplo. Será mucho más persuasivo que correr la boca.
Caray, Grails tiene una generación de prueba incorporada. Si está trabajando en un equipo que usa Grails, ¿cuánto más se necesita vender?
No puedo ver que alguien realmente haya expresado que TDD no se trata de pruebas. TDD-ing se trata de expresar el comportamiento esperado antes de hacer la pequeña modificación que cambia el comportamiento. Esto mejora mucho el diseño y permite enfocar de una manera que nunca antes había experimentado. Obtiene pruebas que protegen sus futuras refactorizaciones y una cobertura del 90% de forma gratuita.
Para aprenderlo, sugeriría (resumir lo que otros han dicho y agregar uno propio):
- visita los blogs y lee los libros mencionados anteriormente
- emparejarse con alguien competente en TDD
- práctica
Practiqué el Bowling kata (ejercicio) por mi cuenta unas 20 veces (unos 30 minutos cada una) antes de comenzar a ver la luz. Comenzó analizando la descripción del tío Bob here . Hay una gran cantidad de katas en el sitio codingdojo.org, incluidas soluciones y debates. ¡Pruébalos!
Para empezar, haga las pruebas unitarias, luego lea sobre cómo hacerlo bien, último, enseñe a su equipo cómo TDD y consiga que participen, porque en mi experiencia, nada es más importante que hacer pruebas unitarias con todo su equipo.
También necesitarás un proceso de compilación adecuado, utilizando un servidor de compilación que construya tu código y ejecute tus pruebas. Te recomiendo usar TeamCity (gratis con limitaciones).
Aprender a realizar buenas pruebas unitarias es la parte más difícil, parte de la cual aprenderá usted mismo (siempre y cuando mantenga las pruebas unitarias) y el resto lo podrá aprender de la búsqueda en Internet.
Sabrá que ha alcanzado su meta cuando NO escriba pruebas unitarias, ya que parte del desarrollo le parecerá incorrecto.
Para tomar una cita de Nike: SOLO HAZLO.
Segundo consejo: nunca confíes en la interfaz de otra persona. Siempre escriba, en el nivel de cada clase, a la interfaz que deseaba que existiera: escriba un adaptador para la implementación real según sea necesario.
Además, me parece útil evitar los valores devueltos en los métodos y pensar en el código en términos de transmisión de mensajes en lugar de llamadas a funciones.
YMMV.
Recuerde, ágil significa que no está completamente agotado en ningún método en particular. Si está trabajando en algo donde los beneficios de TDD no valen la pena (como hacer ediciones de prueba y error en una interfaz Swing), entonces no use TDD.
Un buen lugar para comenzar es leer blogs. Luego compre los libros de las personas que están blogueando. Algunos recomendaría mucho:
"Uncle Bob" Martin y los chicos de Object Mentor: http://blog.objectmentor.com/
PD: obtén Bobs Book Clean Code:
http://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882
Mi amigo Tim Ottinger (antiguo tipo Mentor de objetos) http://agileinaflash.blogspot.com/ http://agileotter.blogspot.com/
Los chicos de Jetbrains: http://www.jbrains.ca/permalink/285
Sentí la necesidad de expandirme sobre esto, ya que todos los demás parecen querer darles su opinión sobre TDD y no ayudarlo en su búsqueda para convertirse en un Jedi-Ninja. El Michael Jordan de TDD es Kent Beck. Él realmente escribió el libro:
http://www.amazon.com/Test-Driven-Development-Kent-Beck/dp/0321146530
también bloguea en:
http://www.threeriversinstitute.org/blog/?p=29
otros partidarios "famosos" de TDD incluyen:
Todos son excelentes personas para seguir. También debería considerar asistir a conferencias como Agile 2010 o Software Craftsmanship (este año se llevaron a cabo al mismo tiempo en Chicago)