Vous êtes sur la page 1sur 10

Si es difícil, aunque cada vez menos, encontrar desarrolladores que escriban test cómo parte

del desarrollo, aun es más difícil encontrar desarrolladores que escriban los test con al
menos el mismo cariño que escriben el resto.
Los test es código igualmente y tenemos el mismo deber de cuidarlo porque tendremos que
mantenerlos a lo largo del tiempo.

Nombre del test


El naming en el desarrollo de software es de las cosas más difíciles porque que genera
mucha controversia cuando estas dentro de un equipo. Para mí los punto clave son estos:
 debe explicar lo que hace, parece sencillo pero no lo es. Generalmente los
desarrolladores tenemos el foco en las tripas de lo que se debe probar y acabamos
contaminando el nombre del test con palabras o conceptos que para nada tienen que
ver con lo que queremos probar en el test y a alguien que no conozca las tripas no
debe importarle.
 nomenclatura, no dediques excesivo tiempo a establecer nomenclaturas, todo
evoluciona y nuestro conocimiento también. En los proyectos suelen participar
varias personas, es complicado que todos los test sigan una misma nomenclatura,
ser exacto en la definición de cómo debe de ser el nombre de un test para mi es lo
de menos.
No hay porque utilizar un patrón de naming concreto, existe Given When Then y
otros. Hay gente que separa ciertas partes del nombre con underscore. Al final lo
mejor es utilizar la estrategia con la que tú y tu equipo os sintáis más cómodos pero
sin dedicarle excesivo tiempo y asumiendo que evolucionará.
 explicar precondiciones, es importante que el nombre del test explique las
precondiciones al realizar en el test, el contexto es una parte fundamental del test y
debe aparecer en el nombre de algún modo. Dos mismas pruebas con
precondiciones diferentes aunque mismo resultado, son dos test distintos. Por lo
tanto deben ser dos nombres distintos. Por ejemplo una precondición puede ser que
la base de datos esta vacía.
 seguir un orden, es más natural si el nombre describe lo que va a hacer y en el
orden que lo hace. Para esto ayuda bastante el patrón Given When Then, por
ejemplo se entiende bien nombres de tests
como GivenAEmptyDataSource_WhenLoadLastSales_ThenShowNoSalesMessage

Cuerpo del test


El cuerpo del test como a mi me parece que se entiende mejor es si es pequeño. Esto no
quiere decir que solo podamos escribir test si son simples, sino que a medida que
escribamos test más complejos o se vaya complicando uno que empezó siendo simple,
mantengamos el método original pequeño y llamemos a otros métodos dándoles también
nombres semánticos.
Por ejemplo suelo utilizar el patrón Given When Then en los test de aceptación y dividir el
test principal en llamadas a unos métodos que componen cada una de las partes del patrón.
[Fact]
public void
GivenAEmptyDataSource_WhenLoadLastSales_ThenShowNoSalesMessage()
{
GivenAEmptyDataSource();
WhenLoadLastSales();
ThenShowNoSalesMessage(); }
private void GivenAEmptyDataSource() {
} private void WhenLoadLastSales() {
} private void ThenShowNoSalesMessage() {
}

Existen multitud de beneficios que se producen al escribir test legibles, cito algunos:

 que tus compañeros lo entiendan, esto es lo más importante, ya lo he dicho en otros


artículos. Lo más importante es escribir código para que lo entiendan tus compañeros no el
compilador.
 documentación, unos tests bien escritos pueden servir de documentación para un
compañero nuevo o para ti mismo en el futuro.
 poder hacer pruebas de acceso, no hay nada mejor que explicar algo que demostrándolo.
Hay empresas donde las pruebas de acceso se basan en unos test que explican lo que una
clase debe de hacer y tienes que realizar el código que validan esos tests. ¿como vas a
realizar bien el ejercicio si no se entienden los tests?, en un escenario como este se ve
claramente la importancia de escribir test legibles y limpios.
 Vas a tener más claro qué debes construir, si sigues TDD y escribes el test antes, al
hacerlo lo más legible posible, tu vas a tener mucho más claro también como se debe
comportar lo que vas a crear y por lo tanto lo vas a crear mejor.

Conclusiones
El código de los test es tan importante o más que el resto. Por lo tanto es necesario cuidarlo,
hacerlo limpio y legible porque nos puede dar muchos beneficios
De lo simple a lo complejo
“SUELO TENER UNA NORMA QUE ME FUNCIONA BASTANTE BIEN QUE ES
IR DE LO SIMPLE A LO COMPLEJO.”
No suelo empezar a crear tests utilizando muchos patrones como los que vamos a ver en
este artículo, intento empezar por la opción más simple.
A medida que voy creando tests y voy identificando código duplicado, acoplado o que los
test se empiezan a leer mal es cuando empiezo a optimizar el código a base de
refactorizaciones.

Opción simple
Como ya escribí en el artículo Escribiendo test legibles, me gusta que los test se mantengan
pequeños porque a medida que son mas grandes se empiezan a leer peor y son más difíciles
de mantener.
Si el objeto a probar (SUT) es una entidad de dominio pequeña, donde el constructor para
crear instancias no tiene muchos parámetros o donde no tenemos que interactuar con el
objeto una vez creado para situarlo en un estado en particular, es bastante sencillo mantener
esta simplicidad.

Object mother
No todos los test son el ejemplo sencillo, existen situaciones donde es necesario escribir
código adicional para configurar el fixture o contexto de la prueba, por ejemplo:

 Cuando necesitamos hacer test de un objeto que tienen colaboradores.


 Si tengo que interactuar con el objeto a probar para situarlo en un estado que me
interesa para una prueba concreta.
 Cuando se hacen pruebas de aceptación sobre la interfaz de usuario y nos interesa
simular la capa de datos usando mocks, stubs ...

En este momento suelo escribir el test primero.

public class MoneyShould {

@Test

public void return_display_name_as_amount_concat_with_currency_symbol() {


Currency EUR = Currency.getInstance("EUR");

Money money = new Money(new BigDecimal("100.00"), EUR);

assertThat(money.getDisplayName(), is("100.00 €"));

El primer problema que suele aparecer cuando existen colaboradores es que se pierde
legibilidad en el test, porque la forma en la que se crea el colaborador es indiferente para el
test del objeto a probar (SUT).
Adicionalmente aparece otro problema, a medida que se crean más tests vamos a tener que
crear el colaborador muchas veces, esto provoca un uso masivo de constructores o métodos
de factoría del propio colaborador.
Cuando creamos manualmente colaboradores nuestros test se vuelven frágiles, al igual que
cuando utilizamos frameworks directamente en los test.
Cuando cambian los constructores o métodos de factoría tenemos que ir por todos esos
sitios actualizando la creación de objetos para adaptarlo.
Una forma de minimizar este problema es utilizar el patrón ObjectMother.
Normalmente utilizo este patrón cuando de un objeto existe un juego de combinaciones
concretas que suelo utilizar en gran número de tests.
Otro tipo de objetos con lo que suele venir muy bien utilizar el patrón Object Mother es con
los típicos maestros.
Refactorizando el ejemplo anterior nos quedaría así:

public class Currencies{

public static Currency EURO() {

return Currency.getInstance("EUR");

public static Currency DOLLAR() {

return Currency.getInstance("USD");
}

public class MoneyShould {

@Test

public void return_display_name_as_amount_concat_with_currency_symbol() {

Money money = new Money(new BigDecimal("100.00"), Currencies.EURO());

assertThat(money.getDisplayName(), is("100.00 €"));

Patrón builder
Pero hay situaciones donde el patrón Object Mother se queda corto:

 Si hay varios objetos a crear, la legibilidad del test aunque mejora, todavía hay
configuración del fixture que hace que la legibilidad del test este lejos de ser la
mejor.
 Si necesitamos muchos escenarios distintos de un mismo objeto, podríamos utilizar
el patrón Object Mother pero tendríamos que crearnos un método por cada
escenario dentro del mismo objeto. Esto provocaría tener un objeto demasiado
grande, muy acoplado a los tests y difícil de mantener.
 Hay valores dentro de la creación del objeto que son irrelevantes para el test y
pueden asignarse unos valores por defecto cualquiera

Cuando se produce alguna de estas situaciones es cuando suelo utilizar el Patrón Builder.
Este patrón te permite tener mucha más flexibilidad para crear el fixture y el objeto que
usamos esta menos acoplado y mejora la legibilidad porque estamos ocultando como se
crea el objeto realmente.
El Patrón Builder se suele implementar con api fluida y mejora la legibilidad donde se esta
utilizando.

public class MoneyBuilder {

private String amount;

private Currency currency;

public MoneyBuilder withAmount(String amount){

this.amount = amount;

return this;

public MoneyBuilder withCurrency(Currency currency){

this.currency = currency;

return this;

public Money build(){

return new Money(new BigDecimal(amount),currency);

public class MoneyShould {

@Test
public void return_display_name_as_amount_concat_with_currency_symbol() {

Money money = new MoneyBuilder()

.withAmount("100.00")

.withCurrency(Currencies.EURO())

.build();

assertThat(money.getDisplayName(), is("100.00 €"));

Creation Method
Es posible que si para configurar el fixture hay que crear más objetos que en los casos
anteriores, van a seguir apareciendo los mismos problemas que teníamos antes aunque en
menor medida:
legibilidad del test, configuración de colaboradores irrelevante para el test actual , etc..
Vamos a ver un caso donde combinamos un objeto producto con los que ya teníamos:

public class ProductBuilder {

private String title = "Product example";

private String amount ="";

private Currency currency = Currencies.EURO();

public ProductBuilder withTitle(String title){

this.title = title;

return this;
}

public ProductBuilder withAmount(String amount){

this.amount = amount;

return this;

public ProductBuilder withCurrency(Currency currency){

this.currency = currency;

return this;

public Product build(){

Money price = new MoneyBuilder()

.withAmount(amount)

.withCurrency(Currencies.EURO())

.build();

Product product = new Product(title);

if (amount!= null && !amount.isEmpty())

product.ChangePrice(price);

return product;

}
}

public class ProductShould {

@Test

public void is_on_sale_if_its_not_free_and_is_marked_as_on_sale() throws Fre


eProductOnSaleException {

Product product = new ProductBuilder()

.withAmount("100.00")

.withCurrency(Currencies.EURO())

.build();

product.markOnSale();

assertThat(product.isOnSale(), is(true));

En este test la cantidad y la moneda del precio es irrelevante para el test, se esta perdiendo
legibilidad.
En este caso suelo utilizar un método de creación dentro de la propia clase de test que libere
completamente al método del test de conocer la configuración del fixture.
Este método de creación lo suelo crear con el prefijo given.

public class ProductShould {

@Test
public void is_on_sale_if_its_not_free_and_is_marked_as_on_sale() throws Fre
eProductOnSaleException {

Product product = givenANonFreeProduct();

product.markOnSale();

assertThat(product.isOnSale(), is(true));

private Product givenANonFreeProduct() {

return new ProductBuilder()

.withAmount("100.00")

.withCurrency(Currencies.EURO())

.build();

Conclusiones
En este artículo hemos visto los patrones que suelo utilizar para optimizar el código de los
test cuando empiezo a identificar código duplicado, demasiado uso de constructores de las
entidades de dominio o si se empieza a perder legibilidad en los test.
No hay que ponerse la venda antes que la herida y solo cuando empezamos a ver problemas
en el código es cuando debemos empezar a optimizar el código.

http://xurxodev.com/como-utilizo-patrones-de-diseno-en-test-unitarios/

Vous aimerez peut-être aussi