Testing en Scala (II) : Scalacheck

Esta semana, en Scalera, os traemos la segunda parte del post que hablaba sobre frameworks de testing en Scala.
Si bien Scalatest aporta un enfoque bastante genérico que permite generar tests usando una gran variedad de specs y suites, Scalacheck permite implementar tests basados en propiedades.

Para poder utilizarlo en tu proyecto de SBT basta con añadir la correspondiente dependencia:

libraryDependencies += "org.scalacheck" %% "scalacheck" % "1.12.4" % "test"

Property-based tests

Los tests basados en propiedades, como su nombre indica, consisten en definir un conjunto de aserciones o propiedades que toda instancia de nuestro tipo de datos debe cumplir.
Si por ejemplo estamos testeando nuestro tipo de datos Car,

case class Car(wheelAmount: Int,speed: Double){

  require(wheelAmount>0,"So, how? tell me how")

  require(speed>=0,"Seriously?Negative speed?")

  def accelerate(): Car = Car(wheelAmount,speed*2)

}

definiríamos nuestra clase CarSpec con el siguiente contenido:

import org.scalacheck.Properties
import org.scalacheck.Prop.forAll

object CarSpec extends Properties("Car") {

  property("has non negative speed") = forAll { (c: Car) =>
    car.speed >= 0
  }

  property("has at least one wheel") = forAll { (c: Car) =>
    car.wheelAmount > 0
  }

  property("doubles its speed") = forAll { (c: Car) =>
    val newCar = c.accelerate()
    newCar.speed = 2*c.speed
  }

}

Como se puede ver, es tan sencillo como definir el nombre de las propiedades que debe cumplir. Mediante la aserción ‘forAll‘, se verificará que, para todos los coches, se deben cumplir las restricciones dictadas.

Generators

Ahora bien, para testear estas propiedades, es necesario generar valores (unos cuantos valores) de tipo Car. De esta tarea se encargan los generadores de ScalaCheck. Estas estructuras conocen la forma de generar valores de un determinado tipo. Por ejemplo,

import org.scalacheck.Gen

val myStringGen: Gen[String] = Gen.alphaString

val stringSample: Option[String] = myStringGen.sample

Si utilizamos el generador predefinido alphaString seremos capaces de generar cadenas de texto aleatorias que contengan solo [a-z].
Con el método sample del generador, obtendremos un posible valor de tipo String.

Gen composition

Pero, ¿y si queremos crear generadores para tipos particulares, como es el caso de Car?
Gen[T] se puede componer para generar valores más complejos. Por ejemplo:

val carGen: Gen[Car] = for {
  wheels <- Gen.choose(4,8)
  speed  <- Gen.oneOf(0.0,50.0,100.0,120.0)
} yield Car(wheels,speed)

Utilizando el método choose[T](min: T,max: T) generamos valores entre ‘min’ y ‘max’.
Con el método oneOf[T](elems: T*) elegimos un elemento de los facilitados en la secuencia de elementos.
Y así, utilizando el elemento obtenido del generador de enteros, y utilizando el elemento obtenido del generador de doubles, podemos construir un Car. Esto no significa que ya estén evaluados todos los posibles valores de los atributos del coche a generar, sino que indicamos, en caso de que nos demanden un coche de este generador, como poder obtenerlos.

o7azz

La punta del iceberg

Como podéis ver, ScalaCheck da bastante juego a la hora de crear tests basados en propiedades, pero el elemento Gen[T] para generar valores ‘semi-aleatorios’ de cierto tipo, puede usarse en más ámbitos, aparte del testing unitario.
En próximos posts veremos como usarlos para la generación de grandes bloques de datos en pruebas de stress para aplicaciones de BigData.

¡Agur de limón! 🙂

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s