domingo, 15 de julio de 2018

Capítulo 34: Refactoring - Parte 2 - TDD By Example

Extract Method

Cuando un método se torna demasiado largo hay muchas posibilidades de poder separar parte del mismo en un nuevo método y hacer una llamada a este método.

La manera de abordar un refactor con «Extract Method» es:

  • Encontrar una parte del código que tenga sentido por si sola en su propio método. Los bloques de los bucles, los bucles completos, los bloques de los condicionales son unos buenos candidatos.
  • Hay que verificar que no se hagan asignaciones a variables temporales que estén declaradas fuera de la visibilidad del la región que se va a extraer.
  • Copiar el código del método al nuevo método.
  • Por cada variable temporal o parámetro del método original que se usa en el nuevo método hay que añadir un parámetro al nuevo método.
  • Hacer la llamada del nuevo método desde el método original.

Los IDE actuales son capaces de hacer este trabajo por ti así que trata de usar su funcionalidad para ahorrar tiempo.

Esta refactorización se utiliza para tratar de entender un código complicado. Separar el código en funciones con nombres descriptivos hace más fácil entender que está haciendo ese código.
También se utiliza para eliminar la duplicación que puede darse en varias partes del código de manera que se pueda reutilizar este bloque.

Habrá momentos que se llevará al extremo hacer «Extract Method» no viendo una manera clara de continuar. En estos casos se puede usar la refactorización contraria «Inline Method» para deshacer la refactorización y volver a empezar con «Extract Method».

Inline Method


Es el paso contrario a «Extract Method». En las ocasiones en las que tiene más sentido que el código esté en el método desde que se hace la llamada.

Los pasos a seguir son:

  • Copiar el método.
  • Pegar el contenido del método sobre la llamada.
  • Reemplazar todo los parámetros con los parámetros que se usen dentro del método al cual se está copiando.

Extract Interface

Para introducir una segunda implementación de una operación se crea una interfaz que contenga definidas las operaciones.

Para llevar a cabo la refactorización:

  • Se declara una interfaz. A veces el nombre coincide con el de la case que existe en cuyo caso habría que renombrar la clase.
  • La clase que existe debe implementar la nueva interfaz.
  • Hay que definir en la interfaz aquellos métodos que sean necesarios.
  • Cambiar las declaraciones de tipo de la clase a la interface cuando sea posible.

Cuando se necesita un refactorizar con «Extract Method» es porque se necesita una segunda implementación. Por ejemplo, si se tiene una clase «Rectangle» y quiere añadir un Oval, entonces puedes crear una interface «Shape» que defina los métodos de «Rectangle».

Move Method


Si un método pertenece realmente a otra clase habría que moverlo a la nueva clase e invocarlo desde donde se utilice.

Los pasos para hacer el refactor:

  • Copiar el método.
  • Pegar el método en la clase objetivo.
  • Si el método tiene referencias al objeto original entonces hay que pasar el objeto como parámetro. Si hay referencia a variables del objeto original entonces son estas las que se pasan como parámetro. Si se asignan variables del objeto original entonces se debería parar y no hacer esta refactorización.
  • Reemplazar el cuerpo del método original con una invocación al nuevo método.

Por ejemplo, calcular el área es responsabilidad de «Shape».

Cada vez que se ve más de un mensaje a otro objeto en un método hay que sospechar. En este caso hay cuatro llamadas a distintos métodos de «$bounds» por lo que se debería mover esta parte del método a la clase «Rectangle».

Y desde el método original se hace la llamada a este nuevo método.

Las tres grandes propiedades de «Extract Method» son:

  • Es fácil de ver sin necesidad de tener un conocimiento profundo de la lógica. Si se ven dos o más mensajes a un objeto es el momento de aplicarlo.
  • Los pasos para hacer el refactor son rápidos y seguros.
  • A veces los resultados son reveladores. “Pero los Rectángulos no hacen cálculos … Oh! Espera. Esto es mejor.”

A veces sólo se querrá mover una parte del método por lo que habrá que usar primero «Extract Method» y move el método.

Method Object

Cuando un método se complica tanto como para tener varios parámetros y variables locales se puede hacer un objeto que absorba la funcionalidad de este método.

Los pasos para refactorizar con «Method Object» son:

  • Crear un objeto con los mismos parámetros que el método.
  • Crear tantos atributos como variables locales.
  • Crear un método llamado «run()» cuyo contenido sea el mismo que el contenido del método original.
  • En el método original crear un nuevo objeto y hacer una llamada al método «run()».

Las refactorizaciones «Method Object» son útiles ya que permiten añadir un nuevo tipo de lógica al sistema. Por ejemplo, se pueden tener varios métodos para calcular el flujo de efectivo en un componente para el cálculo de estos flujos. Se puede practicar una refactorización de estos métodos con «Method Object». También se puede escribir un nuevo tipo de cálculo con sus propias pruebas y con lo que conectarlo será un sólo paso.

Los «Method Object» también son buenos para simplificar el código al que no se puede aplicar la refactorización «Extract Method». En ocasiones habrá un bloque de código que al aplicar «Extract Method» nos encontremos con una función con demasiados parámetros y el aspecto de esta no es mucho mejor que el código original. En este caso, crear un nuevo objeto da un nuevo espacio el cual puede albergar las variables locales sin tener que pasar nada.

Add Parameter


Pues sí, «Add Parameter» es un tipo de refactorización:

  • Si el método es de una interfaz entonces se añade el parámetro a la interfaz.
  • En los lenguajes compilados habrá errores que adviertan del código que se necesita cambiar. Si no habrá que usar las funcionalidades del IDE para buscar aquellos lugares.

Añadir un parámetro es a veces un paso de extensión. Primero se tiene el primer caso ejecutándose sin necesidad de parámetros para más tarde, pasar un parámetro que añade más información para realizar la operación correctamente.

Añadir un parámetro puede ser parte de una migración una representación de un dato a otro. Primero se añade el parámetro, entonces se borra todos los usos del parámetro viejo y después el parámetro viejo.

Method Parameter to Constructor Parameter


Mover parámetros de un método o métodos al constructor es otra manera de refactorizar.

Los pasos son:

  • Añadir un parámetro al constructor.
  • Añadir un atributo con el mismo nombre que el parámetro.
  • Asignar valor del parámetro al atributo en el constructor.
  • Cambiar todas las referencias del parámetro en el método por referencias al atributo.
  • Cuando ya no haya más referencias al parámetro se borra el parámetro de la firma del método y todos los que hacen la llamada.

En los casos en los que el mismo parámetro se pasa en diferentes métodos dentro del mismo objeto se puede simplificar la API pasando el parámetro una sóla vez (eliminando la duplicación). Esta refactorización se puede aplicar de forma inversa si se encuentra con que un atributo sólo se utiliza en un método.

viernes, 13 de julio de 2018

Capítulo 34: Refactoring - Parte 1 - TDD By Example

La parte más importante del «refactoring» es saber el porqué podrías querer usarlo.

En TDD se utiliza de manera que no se cambia la semántica del programa bajo ninguna circunstancia. Así que las pruebas que ya han pasado definen la semántica que hay hasta ahora así que, por ejemplo, se pueden reemplazar las constantes por variables y llamar a esta operación «refactorización» porque esta no cambia el conjunto de pruebas que pasan.

Reconciliar las diferencias


Cuando hay dos piezas de código que son muy similares se puede adoptar la estrategia de, gradualmente, acercarlas hasta que son absolutamente idénticas y en ese caso unificarlas.

Habrá refactorizaciones que sean obvias y que apenas cambien el comportamiento del sistema mientras que otras obligarán a revisar el control de flujo y los datos.

TDD brinda las herramientas necesarias para hacer refactorizaciones en pasos pequeños y tener «feedback» en el momento por lo que se deberían evitar los saltos de fé con refactorizaciones de dimensiones que no podamos controlar.

Las refactorizaciones ocurren en todos los ámbitos:
  • Si dos estructuras de bucle son iguales se pueden hacer idénticas y se puede unir.
  • Si dos ramas de un condicional son similares se pueden hacer idénticos y eliminar el condicional.
  • Si dos métodos son similares se pueden hacer idénticos y eliminar uno.
  • Si dos clases son similares se pueden hacer idénticas y eliminar una.

A veces se necesita abordar la reconciliación de las diferencias al revés, pensar como el último paso del cambio podría ser trivial y continuar así. Por ejemplo, si se quiere eliminar varias subclases el paso trivial es si la clase no contiene nada, en este caso, la subclase se puede reemplazar por la superclase sin cambiar el comportamiento del sistema. Para vaciar la clase el método debe ser idéntico a uno en la superclase. Entonces se vacían todas las subclases y, cuando todas estén vacías, se puede reemplazar las referencias de estas por la superclase.

Aislar el cambio


En el momento que se quiere hacer un cambio hay que identificar en qué parte del código se va a implementar, esto es aislar la parte que va a cambiar.

Habrá ocasiones en las que hemos aislado y hecho el cambio este resulte tan trivial que se pueda deshacer el aislamiento. Si encontramos que todo lo que se necesitaba era devolver una variable in «findRate()» deberíamos considerar la inclusión de todos los sitios donde se usa y borrarlo. Antes de borrarlo hay que valorar si tener un método adicional con el valor de tener un concepto explícito adicional  en el código.

Algunas estrategias para aislar el cambio son «Extract Method», «Extract Object» o «Method Object».

Migrar los datos


Las migraciones de datos deberían duplicarse de manera temporal.

Una estrategia para la migración es desde el interior hacia el exterior. En esta estrategia se cambia la representación interna para, posteriormente, cambiar la interface externa visible.

  • Añadir una nueva variable de instancia en el nuevo formato.
  • Asignar a la nueva variable en todos los lugares donde la vieja variable estaba asignada.
  • Usar la nueva variable en todos los lugares donde la vieja variable se usaba.
  • Borrar la vieja variable.
  • Cambiar la interfaz externa para reflejar la nueva variable.

En otras ocasiones la estrategia será cambiar del exterior al interior. Se cambia la interface externa visible para, posteriormente, cambiar la representación interna. Entonces se debería:

  • Añadir un parámetro en el nuevo formato.
  • Traducir la representación interna del viejo formato al nuevo formato.
  • Borrar el parámetro antiguo.
  • Reemplazar el uso del viejo formato con el nuevo.
  • Borrar el formato antiguo.

Las migraciones de datos de uno a una colección («One to Many») siempre son complejas de abordar. Por ejemplo, en el caso se pasar de ejecutar una prueba a tener un conjunto de pruebas en xUnit. Se debería empezar:

La clase «TestSuite», la cual está en la parte «One» de «One to Many», tiene la siguiente implementación.

Ahora empezamos con la duplicación de datos. Primero inicializamos la colección de pruebas.

En todos los lugares donde se asigna un valor a la variable «test» añadimos a la colección.

Ahora es el momento de usar la colección de «test» en vez de usar la variable «test». En este caso el cambio preserva la semántica del código ya que sólo hay un elemento en la colección.

Finalmente se limpia el código de las variables de «test» que no se usan.

lunes, 9 de julio de 2018

Capítulo 33: Design Patterns - Collecting Parameter - TDD By Example

Para recopilar el resultado de una operación que está extendida entre varios objetos se puede añadir un parámetro a la operación en el cual se puedan recopilar estos resultados.

En xUnit no se necesito que «TestResult» recopilará el resultado de varias pruebas hasta que no se tuvieron varias pruebas.

A medida que se espera que los resultado crezcan se puede encontrar la necesidad de usar «Collecting parameter». Por ejemplo, si se quiere escribir una expresión y todo lo que necesitamos es escribir un texto plano entonces la concatenación es suficiente.

Teniendo la clase Sum el método «toString()».

Si se quiere añadir sangría en forma de árbol en la expresión tendremos que modificar la prueba:

En este caso tendremos que implementar el patrón «Collecting Parameter».

Capítulo 33: Design Patterns - Composite - TDD By Example

Para implementar un objeto cuyo comportamiento sea igual que el comportamiento de una lista de otros objetos se crea un «Imposter» para el objeto de la lista.

Por ejemplo, una transacción incrementa un valor.

Una cuenta calcula su balance sumando los valores de sus transacciones.

Un cliente podrá tener una o más cuentas y, en cualquier caso, podrá ver el balance general de todas sus cuentas. La implementación más obvia sería crear una nueva clase «OverallBalance» que sume el balance de cada una de las cuentas.

Aunque a primera vista puede ser un poco más difícil de ver, existe duplicación de código. Si una «Account» y «Transaction» implementaran la misma interfaz, por ejemplo «Holding» la duplicación desaparecería ya que el «OverallBalance» podría ser una cuenta que contiene cuentas.

La transacción puede implementar el método «balance()».


Una cuenta está compuesta por una o más «Holding».

El olor de «Composite» viene ilustrado por ejemplo anterior. Las transacciones no tienen un balance en el mundo real pero aplicando el patrón los beneficios para el diseño son sustanciales ya que elimina la duplicidad. La traducción no viene del mundo real pero hace el código mucho más simple.

No siempre es tan obvio en qué caso una colección de objetos es una simple colección o se tiene un «Composite». A medida que se va cogiendo experiencia en la refactorización se podrá apreciar la duplicación de código y se podrá valorar si hay que aplicar el patrón «Composite».

jueves, 5 de julio de 2018

Capítulo 33: Design Patterns - Imposter - TDD By Example

Para introducir nuevas variaciones en un proceso se puede introducir un nuevo objeto con el mismo protocolo que el objeto que existe pero con la nueva implementación.

Si la variación se puede aplicar en un lugar que sea obvio y además no se está duplicando el código entonces se puede aplicar la variación con una instrucción «if». En otras ocasiones la variación requerirá del cambio en varios métodos.

Se deberá escribir una prueba para la variación ya que se necesita representar el nuevo escenario. Por ejemplo si se está probando un editor gráfico y ya está probado que se dibujan correctamente los objetos de tipo rectángulo con una prueba como la siguiente.

Si queremos dibujar un óvalo, en este caso el Impostor debería reemplazar el objeto de clase «RectangleFigure» por «OvalFigure».

Generalmente, detectar la posibilidad de utilizar un «Imposter» requiere de un poco de conocimiento. La percepción de Ward expone que un vector de dinero puede actuar como dinero es precisamente ese momento.

Dos ejemplos de «Imposter» que vienen durante la refactorización:

  • «Null Object»: se puede tratar la ausencia de datos al igual que la presencia de datos.
  • «Composite»: se puede tratar una colección de objetos como si de un objeto se tratase.

Encontrar dónde aplicar «Imposter» durante la refactorización reduce la duplicación de código al igual que toda refactorización es para eliminar la duplicación.

Capítulo 33: Design Patterns - Factory Method - TDD By Example

Cuando se requiere de flexibilidad a la hora de crear un nuevo objeto se puede crear este en un método en vez de usar el constructor.

Los constructores no son expresivos mientras que los método pueden comunicar la intención. Además, tiene la ventaja que el método no tiene porqué devolver la misma clase si no que puede utilizar una clase diferente para crear el objeto.

En el ejemplo de la clase «Money» se podía devolver un objeto de una clase diferente. Se tenía la siguiente prueba.

Se quería introducir la clase «Money» pero existía el bloqueo de crear una instancia de «Dollar». Este bloqueo se podría solventar añadiendo un nivel de indirección a través de un método de manera que se puede ganar la flexibilidad de devolver un objeto de una clase diferente sin cambiar la prueba.

Este método se llama factoría porque crea objetos.

El inconveniente de usar «Factory Pattern» es precisamente su indirección y recordar que el método está creando un objeto aunque no se vea como un constructor. Este patrón sólo se debería usar cuando se necesite la flexibilidad de crear nuevos objetos. De otra manera, los constructores funcionan bien para crear objetos.

miércoles, 4 de julio de 2018

Capítulo 33: Design Patterns - Pluggable Selector - TDD By Example

Para ejecutar un comportamiento diferente para diferentes instancias se puede almacenar el nombre del método y ejecutarlo dinámicamente.

Crear subclases en un mecanismo pesado para capturar pequeñas variaciones. Por ejemplo, si se tiene una clase «Report» se podría tener varios tipos de informes

Una alternativa es tener una sola clase con instrucción «switch». Dependiendo del valor del campo, se puede invocar un método de forma dinámica. El nombre del método aparecería en:
  • La creación de la instancia.
  • La instrucción «switch».
  • La definición del propio método.
Cada vez que surge la necesidad de aplicar un nuevo comportamiento hay que definir el nuevo método y modificar la instrucción «switch».

La solución aplicando el patrón «Pluggable Selector» pasa por invocar el método dinámicamente:

Ahora hay una dependencia entre los métodos y el nombre de los métodos aunque elimina la instrucción «switch».

Uno de los principales problemas de aplicar este patrón es la trazabilidad del código. Sólo se debería usar el patrón «Pluggable Selector» cuando se de una situación bastante sencilla en la que un conjunto de subclases tengan un único método.

Capítulo 33: Design Patterns - Pluggable Object - TDD By Example

La manera más simple de expresar una variación es hacerlo de manera explícita a través de condicionales:

En el momento que se hace una variación de manera explícita esta se va a comenzar a extender por todo el código añadiendo complejidad al mismo. Por esta razón la segunda vez que se vea un condicional de este tipo es el momento de utilizar «Plugabble Object».

Cuando en su día Erich Gamma y Kent Beck escribieron un editor gráfico dieron con un problema donde aplicar este patrón. Cuando estaban desarrollando la herramienta de selección si estaban sobre una figura y se presionaba el botón del ratón el resto de movimientos del ratón arrastraba la figura hasta soltar el botón. Si no había una figura entonces se estaba seleccionando un grupo de figuras. Arrastrar el ratón provocaba incrementar el rectángulo de selección hasta soltar el botón y en ese momento se seleccionaban todas las figuras que estuvieran en el rectángulo de selección.

El código inicial podría ser algo similar al siguiente.

En este caso se puede apreciar como la variación de si hay una figura seleccionada se extiende por el código. Una posible solución para resolver este problema es crear un «Pluggable Object», un «SelecctionMode» con dos implementaciones «SingleSelection» y «MultipleSelection».


Se tendrá que implementar una interfaz con las dos implementaciones.

Capítulo 33: Design Pattern - Template Method - TDD By Example

Hay ocasiones en las que se implementa un método que definen una serie de operaciones. Si tenemos otro método que implementa las misma serie de operaciones pero con algunas partes variables entonces se puede hacer un refactorización en la que se crea una clase padre con el método con las partes comunes y las subclases implementen las partes variables.

En el ejemplo de xUnit se puede apreciar la secuencia de operaciones de la clase padre «TestCase».

Las clases hijas son las que implementan los distintos métodos.

La pregunta que surge es cuándo se debería escribir una implementación por defecto. En los casos de los métodos «setUp()», «tearDown()» no tienen operaciones por defecto aunque son necesarios para el funcionamiento de la secuencia por lo que habría que implementarlos.

Si la secuencia de operaciones necesita una operación la cual necesita una implementación específica entonces se debería advertir declarando el método abstracto o implementando el método y lanzando una excepción.

lunes, 2 de julio de 2018

Capítulo 33: Design Pattern - Null Object - TDD By Example

Hay ocasiones en las que los programadores tendrán que comprobar si una variable tiene un valor nulo y esta comparación puede repetirse a lo largo de diversas partes del código.

En el siguiente ejemplo se puede apreciar como, cada vez que se hace una búsqueda de una campaña se tiene que hacer una comprobación por si fuera nulo el valor devuelto.


Una posible solución es representar este caso especial con un objeto.


Cada vez que alguien utilice el método «find()» del repositorio, en aquellos casos en los que no se encuentre la campaña, se devolverá el objeto del caso especial.


Con esta estrategia ya no hay que preocuparse de realizar la comprobación ya que esta se realiza en un único sitio y en caso de ser nula devuelve el objeto especial. Con lo que el código original queda más sencillo.

Capítulo 33: Design Pattern - Value Object - TDD By Example

Si dos objetos A y B hacen referencia a un tercero C y A cambia el estado de C entonces B no debería confiar en el estado de C.

Una posible solución es no compartir los objetos en los que se confía si no hacer copias de los mismos. Esto lleva a un mayor uso de espacio y tiempo. Otra solución sería utilizar el patrón Observer de manera que se avise explícitamente de la modificación cuando se produzca un cambio. Esto complica los flujos de control y la lógica.

Otra solución es tratar al objeto como un objeto que no cambia. De esta manera se podrá hacer referencia a él sin miedo a que cambie el estado.

Cuando se implementa el patrón «Value Object» cada operación que se define sobre un objeto devuelve un nuevo objeto sin alterar el objeto original desde el que se hizo la operación.

Cuando se usa un patrón «Value Object» todos los objetos que lo implementan deberían implementar un mecanismo para comprobar la igualdad entre dos de ellos. Cinco euros deberían ser iguales a cinco euros independientemente de si son referencias al mismo objetos o si son objetos distintos con el mismo valor.

Capítulo 33: design Patterns - Command - TDD By Example

Cuando una operación es más complicada que una simple llamada a un método se crea un objeto que albergue la operación y se ejecuta. Se encapsula la funcionalidad en el objeto y se le pasan los parámetros adecuados para su correcto funcionamiento.

Este patrón establece una interfaz común que permite ejecutar las operaciones de forma uniforme así como extender el sistema con nuevas operaciones de una forma sencilla.

Capítulo 33: Design Patterns - TDD By Example

Los patrones de diseño son soluciones que se aplican a problemas comunes. Estos no son un diseño final si no es una descripción de cómo resolver un problema y que se puede aplicar en diversa situaciones. Se pueden considerar como buenas prácticas para resolver estos problemas comunes cuando se está diseñando una aplicación o sistema.

Se van a presentar los siguientes patrones de diseño:

  • Command: representa la invocación de un operación como un objeto no simplemente como un mensaje.
  • Value Object: evita problemas creando objetos que nunca cambian una vez que se han creado.
  • Null Object: representa el caso base de una operación por un objeto.
  • Template Method: representa secuencias invariables de operaciones con un método abstracto el cual se define a través de la herencia.
  • Pluggable Object: evita subclases invocando dinamicamente diferentes métodos para diferentes instancias.
  • Factory Method: crea un objeto llamando a un método en vez de a un constructor.
  • Composite: representa la composición del comportamiento de una lista de objetos con un objeto.
  • Collecting Parameters: 
  • Imposter: introduce variaciones introduciendo a nueva implementación de un protocolo existente.

Patrón Escritura de prueba Refactor
Command X
Value Object X
Null Object X
Template Method X
Plugabble Object X
Pluggable Selector X
Factory Method X X
Composite X X
Collecting Parameter X X
Imposter X X

domingo, 1 de julio de 2018

Capítulo 32: xUnit Patterns - TDD By Example

La manera de comprobar si un código funciona correctamente es escribir un expresión binaria que juzgue si el código funciona.

En el caso de las pruebas automatizados significa que hay que tener todas esas expresiones almacenadas en forma de pruebas de manera que se lance un instrucción o se pulse un botón de manera que se ejecuten y comprueben todas esas expresiones. Esto sugiere:

  • Que las expresiones tienen que ser binarias. Funciona o no funciona.
  • El estado de las expresiones las comprueba el entorno llamando a alguna variantes de un método «assert()».

Las afirmaciones tienen que ser lo más precisas posibles. Si un el área de un rectángulo tiene que ser 50, di que este debería ser 50 y (assertTrue($rectangle->area() == 50)) y no algo maś genérico como (assertTrue($rectangle->area() != 0)).

Es recomendable revisar si la librería xUnit tiene una afirmación especial para comparar valores así una entrada para un mensaje de error en el que podamos indicar qué está sucediendo. Por ejemplo,



Las pruebas no deberían en ningún caso depender de la implementación por lo que si esta cambia, las pruebas, deberían seguir ejecutándose normalmente.

Fixture

Cuando tenemos varias pruebas que utilizan un objeto común podemos sobreescribir el método «setUp()» de la prueba e inicializar el objeto común.

Un ejemplo realmente simple podría ser inicializar un objeto común a varias pruebas.


En este caso podríamos deshacernos de la duplicación:


La relación entre las subclases de «TestCase» y las instancias de aquellas subclase es una de las partes más complicadas de xUnit. Cada nuevo tipo de «fixture» debería ser una nueva subclase de «TestCase». Cada nuevo «fixture» se crea en una instancia de esa subclase, se usa y se descarta.

En el ejemplo, si queremos escribir  una prueba para un rectangulo que no esté vacío entonces habrá que escribir una nueva clase, «NormalRectangleTest». En general, si hay un «fixture» ligeramente diferente entonces hay que crear una nueva clase de «TestCase».

External Fixture

La manera de liberar recursos externos en el «fixture» es sobreescribir el método «tearDown()» y liberar estos recursos.

El objetivo de las pruebas es comprobar si el código funciona pero las pruebas tienen que ejecutarse de manera independiente por lo que el estado debe ser siempre el mismo antes y después de ejecutarse una prueba.

Por ejemplo, si se abre un archivo al final de la ejecución de la prueba debería cerrarse.


Si «MyFile» se usa en varios pruebas entonces se podría hacer parte del «fixture».


Primero, si se utiliza en varias pruebas se puede apreciar que hay una duplicación de la cláusula «finally» que indica que se está perdiendo algo en el diseño. Segundo hay que cerrar el archivo mediante el método «close()» lo que se puede olvidar con cierta facilidad. Por último hay varios componentes en la prueba inicial, que son la parte «try», la parte «finally» y el propio cierre.

xUnit garantiza ejecutar el método «tearDown()» después de ejecutar el método de prueba e independientemente de lo que pase en este método. Así que se puede transformar el código anterior en el siguiente.


Test Method

Los método de prueba, por convección, se representan con un método que comienza con la palabra «test».

En el caso de utilizar «fixtures» todas las pruebas que comparten una misma «fixture» serán método de la misma clase. Las pruebas que tengan un «fixture» distinto tendrán una clase distinta.

Los métodos de prueba deberían tener un nombre descriptivo que comunique la intención de lo que se está probando.

Los método de prueba deberían ser fáciles de leer y deberían tener pocas líneas. Si un método de prueba se vuelve complejo de entender es posible que se necesite hacer varios métodos de prueba más sencillos.

Exception Test

Hay ocasiones en la que queremos probar si una excepción se lanza durante una ejecución. La prueba fallará sólo si la excepción no se lanza.


Alltest

Aunque actualmente la mayoría de IDE son capaces de ejecutar todos las pruebas.

Si se añade un prueba a un paquete y se añade un método de pruebas la siguiente vez que se ejecuten todos las pruebas también se debería ejecutar esta nueva prueba. En el caso de que el IDE no xUnit no soporte esta característica cada paquete debería declarar una clase «AllTest» que implemente un método estático «suite()» que devuelva una «TestSuite».

Se puede añadir este «AllTest» al método «main()» así la clase puede ejecutarse desde el IDE o la línea de comandos.