jueves, 16 de noviembre de 2017

Hamcrest nivel intermedio. Como hacer tu propio Matcher

Ya hemos hablado anteriormente de Hamcrest y sus posibilidades aquí. Ahora daremos un paso más y veremos como hacer nuestros propios Matchers. Es verdad que Hamcrest tiene multitud de métodos pero puede sernos necesario algún método concreto, bien porque sea una cualidad específica de nuestras pruebas o por que dicha comprobación se realice multiples veces. 

Es posible hacerlo de varias formas y empezaremos con la más básica: BaseMatcher. Debemos extender la clase para crear nuestra prueba. Para los ejemplos usaremos un objeto 'Car' que tiene un atributo entero 'wheels' y un atributo String 'colour'. 

@Test
public void equal() {
 Car car = new Car(3, "white");
 assertThat(car, hasEnoughWheels());
}
private Matcher<Car> hasEnoughWheels() {
 return new BaseMatcher<Car>() {
  public boolean matches(final Object item) {
   final Car car = (Car) item;
   return car.getWheels() >= 4;
  }
  public void describeTo(final Description description) {
   description.appendText("A car must have at least 4 wheels");
  }
  public void describeMismatch(final Object item, final Description description) {
   description.appendValue(((Car) item).getWheels() + " are not enough.");
  }
 };
}

Si ejecutamos la prueba, tendremos la siguiente salida obtendremos:

java.lang.AssertionError: 
Expected: A car must have at least 4 wheels
     but: "3 are not enough."
 at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
 ...

La diferencia entre BaseMatcher y nuestra siguiente clase, TypeSafeMatcher, es que esta última verificará el objeto que le pasamos y lo casteará por nosotros. Pero a cambio deberemos implementar otros métodos (matchesSafely y describeMismatchSafely por matches y describeMismatch).

@Test
public void equal() {
 Car car = new Car(3, "white");
 assertThat(car, hasCorrectColour("black"));
}
private Matcher<Car> hasCorrectColour(final String colour) {
 return new TypeSafeMatcher<Car>() {
  protected boolean matchesSafely(final Car car) {
   return colour.equals(car.getColour());
  }
  public void describeTo(final Description description) {
   description.appendText("A car should have the colour " + colour);
  }
  protected void describeMismatchSafely(final Car item, final Description mismatchDescription) {
   mismatchDescription.appendText("But was: " + item.getColour());
  }
 };
}

La salida sería:

java.lang.AssertionError: 
Expected: A car should have the colour black
     but: But was: white
 at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
 ...

Luego hay dos clases más DiagnosingMatcher y TypeSafeDiagnosingMatcher que siguen siendo básicamente lo mismo, pero con la cualidad que puedes crear el objeto description dentro del pripio método 'matches'. De esta forma implementamos un único método y no dos.

Y por último está CustomMatcher y su versión TypeSafe, que estan pensadas principalmente para el uso de clases internas. Es decir crear Matcher no reutilizables.

@Test
public void equal() {
 Car car = new Car(3, "white");
 Coche coche = new Coche();
 Matcher<Integer> aPositiveInt = new CustomMatcher<Integer>("a positive int") {
  public boolean matches(final Object object) {
   return object instanceof Integer && !((Integer) object < 0);
  }
 };
 assertThat(-1, aPositiveInt);
}

No hay comentarios:

Publicar un comentario