Pruebas generativas

¿Qué son las pruebas generativas?

Las pruebas generativas son pruebas sobre código que se realizan usando datos “aleatorios” sobre las funciones que nosotros escribimos. La idea es probar estos datos “aleatorios” contra propiedades de la salida deseada de las funciones.

En Clojure existe una biblioteca llamada clojure.test.check, la cuál se encarga de proveer herramientas para construir estas pruebas generativas.

Las pruebas generativas, básicamente se encargan de separar la definición del dominio de entrada respecto al concepto de generadores y validadores.

Pruebas en clojure

Clojure, a través del espacio de nombres clojure.test, provee funciones y macros para realizar pruebas unitarias en sobre las funciones que implementemos en clojure.

Las pruebas en clojure generalmente lucen de la siguiente forma:

(require '[clojure.test :refer [deftest]])
(defn add
  "Realiza la suma de |x| con |y|"
  [x y]
  (+ x y))

(deftest add-x-to-y
  (is (= 5 (add 2 3))))

El ejemplo anterior, es un ejemplo pequeño de como realizar pruebas unitarias en clojure. Pero en ocasiones esto no es suficiente, para esto, se define una nueva forma de probar el código que implementemos utilizando datos generados de forma “aleatoria” para introducirla a nuestras funciones y a partir de las propiedades de la salida, determinar si estas funciones son correctas o no. Este tipo de pruebas se les conoce como pruebas generativas.

El espacio de nombres clojure.test.check (una biblioteca inspirada en Quickcheck de Haskell), provee una serie de funciones y macros para poder crear pruebas generativas sobre el código que nosotros escribimos. Estas pruebas generativas como mencionamos previamente, están sustentadas sobre la prueba de propiedades de las salidas de una función.

Ejemplos de como realizar pruebas generativas usando clojure.test.check las podemos encontrar en la documentación del mismo proyecto, la cuál podemos acceder a través de esta liga.

Aún así se agrega a este documento un ejemplo pequeño como muestra:

(require '[clojure.test.check :as tc]
         '[clojure.test.check.generators :as gen]
         '[clojure.test.check.properties :as prop])

(defn ascending?
  "clojure.core/sorted? doesn't do what we might expect, so we write our own
  function"
  [coll]
  (every? (fn [[a b]] (<= a b))
          (partition 2 1 coll)))

(def property
  (prop/for-all [v (gen/vector gen/int)]
    (let [s (sort v)]
      (and (= (count v) (count s))
           (ascending? s)))))

;; test our property
(tc/quick-check 100 property)

Entendiendo el como usar las pruebas generativas

Del código anterior, se invocan los siguientes espacios de nombres:

(require '[clojure.test.check :as tc]
         '[clojure.test.check.generators :as gen]
         '[clojure.test.check.properties :as prop])

Los cuáles son los encargados de proveer funciones y macros para la generación y prueba de funciones. De estos espacios de nombres podemos observar las siguientes características:

tc
Contiene una única función quick-check que es utilizada probar funciones con valores generados por una función generador.
gen
Contiene funciones para generar datos basados en propiedades.
prop
Provee sugar macros para definir parámetros y funciones para ser probadas por los generadores.

Más info sobre estos espacios de nombres, pueden ser consultados en clojure.test.check

Una vez identificadas los distintos tipos de funciones y macros provistos por clojure.test.check, procedemos a definir una función y una propiedad a probar para esa función.

(defn ascending?
  "clojure.core/sorted? doesn't do what we might expect, so we write our own
  function"
  [coll]
  (every? (fn [[a b]] (<= a b))
          (partition 2 1 coll)))

Lo anterior define una función que verifica que para cada para par dentro de la colección que pasamos como parámetro, esta siempre sea ordenada, i.e. exista un ordenamiento.

Una vez hecho, procedemos a escribir la propiedad:

(def property
  (prop/for-all [v (gen/vector gen/int)]
    (let [s (sort v)]
      (and (= (count v) (count s))
           (ascending? s)))))

La cuál verifica que podamos ordenar una colección y además que dicha colección se encuentra ordenada de forma ascendente. La colección usada es generada por (gen/vector gen/int), la cuál es una combinación de generadores que dice que genere un vector de enteros y lo guarde en una variable v, la cuál después se la pasamos a la forma para evaluar nuestra expresión.

Hecho lo anterior, se utiliza la función tc/quick-check, indicándole el número de muestras a generar, en este caso le decimos que 100 de la siguiente forma:

(tc/quick-check 100 property)

Hasta aquí vamos a detener este artículo introductorio, esperemos que podamos continuar con la generación de bases de datos de batería para probar nuestro código, para ello intentaremos hacer uso de las pruebas generativas de clojure.test.check.

Anuncios

Prettify-symbols-mode

Desde la versión 24.4 de emacs se incluye un nuevo modo llamado prettify-symbols-mode, el cuál esta planeado para remplazar la representación de varios identificadores/símbolos usados en los lenguajes de programación por caracteres más estéticos.

Por ejemplo, en el modo lisp (emacs-lisp también), al escribir la palabra reservada lambda, visualmente se transforma en el símbolo λ, pero que en el texto se sigue conservando la palabra escrita.

Esto permite que escribir en diversos lenguajes tenga una notación un poco más “matemática” y visualmente más estética respecto a escribirlo en otros editores.

Para activar esta características, por ejemplo, en el modo lisp hay que sobreescribir la variable lisp--prettify-symbols-alist de la siguiente forma:

(defconst lisp--prettify-symbols-alist
  '(("lambda"  . ?λ)))

Esto significa que la palabra lambda va a ser remplazada por el simbolo λ. Pero también se puede agregar más remplazos para otros modos:

(add-hook clojure-mode-hook
            (lambda ()
              (push '(">=" . ?≥) prettify-symbols-alist)))

En caso de que queramos activar por defecto prettify-symbols en la mayoría de los modos por defecto de emacs, agregamos la siguiente linea a nuestra configuración de emacs:

(global-prettify-symbols-mode +1)

Clojure Spec

Primero que nada, ¿Qué es clojure spec? Es una biblioteca de clojure que especifica la estructura de los datos, permite la validación de los mismos y puede generar datos a partir del spec.

clojure.spec (o cljs.spec) esta incluida en el core de clojure a partir de la versión 1.9 (aún en estado alpha), así que no es necesario incluir alguna otra biblioteca.

clojure.spec va a permitir que definamos predicados sobre los datos que estamos modelando, permitiendo que estos sean validados en tiempo de ejecución de acuerdo a los predicados definidos. Esto es mucho más poderoso que tener definiciones basadas en tipos.

clojure.spec además permite tener pruebas generativas para probar que las funciones y los datos que definamos funciones de acuerdo a la abstracción de características (predicados) que tenga la información. Se podría decir que clojure.spec tiene los siguientes objetivos de acuerdo a su documentación:

  • Comunicación
  • Especificación unificada en varios contextos
  • Maximizar el apalancamiento a partir del esfuerzo de la especificación
  • Minimizar la intrusión
  • Establecer e iniciar un diálogo sobre el cambio de semánticas y compatibilidad.

Lo anterior debería de automatizar lo siguiente:

  • Validación
  • Reporte de errores
  • Destructuring
  • Instrumentación
  • Generación de datos de prueba
  • Generación de pruebas generativas

Todas estas características permiten el desarrollo de software con un alto nivel de certidumbre, ya que evita la necesidad de crear pruebas unitarias (siempre es difícil establecer cuales son los casos interesantes a probar) y permite tener pruebas generativas con casos de prueba generados a partir de la abstracción de características de los datos modelados.

Three More List Manipulation Exercises

Programming Praxis

We have three more list-manipulation exercises today, for those students midway through their beginning programming course who need practice with linked lists:

  1. Define a function that takes two lists, the second of which is a list of non-negative integers the same length as the first list, and returns a list of elements from the first list, in reverse order, each repeated a number of times as specified by the corresponding element of the second list.
  2. Rearrange the integers in a list so that all the odd numbers appear before all the even numbers, with both odd and even numbers in the same relative order in the output as they were in the input.
  3. Write a function that returns the nth item from the end of a list.

Your task is to write the three specified programs. When you are finished, you are welcome to read or run a suggested solution…

Ver la entrada original 421 palabras más

Two Interview Questions

Programming Praxis

It’s been a while since we had any interview questions. We have two today, since they are so simple:

1) Given a number n, find the smallest 3-digit number such that the product of its digits is equal to n. For example, given n = 100, the solution is 455.

2) Given two arrays, one with n items and one with n+2 items including the same items as the array with n items plus two additional items, find the two additional items. Assume none of the items are duplicates.

Your task is to write solutions to the two interview questions given above. When you are finished, you are welcome to read or run a suggested solution, or to post your own solution or discuss the exercise in the comments below.

The simplest solution for the first question uses three nested loops, computing the product and returning the…

Ver la entrada original 700 palabras más

Conceptos de código limpio

Después de observar los cambios que se han ido realizando en los proyectos de código de mis alumnos, me puse a pensar en el concepto de código limpio el cuál para mi significa lo siguiente:

El código limpio es aquel código que es fácil de leer, fácil de entender y muestra un rendimiento óptimo.

¿Pero qué piensan otros programadores acerca del código limpio? para esto, voy a tomar algunas citas del libro Código limpio de Robert C. Martin.

Empecemos con Bjarne Stroustop, desarrollador del lenguaje de programación C++ y autor de The C++ Programming Language

Me gusta que mi código sea elegante y eficaz. La lógica debe ser directa para evitar errores ocultos, las dependencias deben de ser mínimas para facilitar el mantenimiento, el procesamiento de errores completo y sujeto a una estrategia articulada, y el rendimienot debe ser óptimo para que los usuarios no tiendan a estropear el código con optimizaciones sin sentido. El código limpio hace bien una cosa.

Grady Booch, autor de Object Oriented Analysis and Design with Applications

El código limpio es simple y directo. El código limpio se lee como un texto bien escrito. El código limpio no oculta la intención del diseñador si no que muestra nítidas abstracciones y líneas directas de control.

Big’ Dave Thomas, fundador de OTI, el padrino de la estrategia Eclipse.

El código limpio se puede leer y mejorar por parte de un programador que no sea su autor original. Tiene pruebas de unidad y aceptación. Tiene nombres con sentido. Ofrece una y no varias formas de hacer algo. Sus dependencias son mínimas, se definen de forma explícita y ofrece una API clara y mínima. El código debe ser culto en función del lenguaje, ya que no toda la información necesaria se puede expresar de forma clara en el código.

Michael Feathers, autor de Working Effectively with Legacy Code.

Podría enumerar todas las cualidades del código limpio pero hay una principal que engloba a todas ellas. El código limpio siempre parece que ha sido escrito por alguien a quien le importa. No hay nada evidente que hacer para mejorarlo. El autor del código pensó en todos los aspectos posibles y si intentamos imaginar alguna mejora, volvemos al punto de partida y sólo nos queda disfrutar del código que alguien a quien le importa realmente nos ha proporcionado.

Después de observar estas citas y algunas más, creo que las características que debe cumplir el código limpio son las siugientes:

  • Legible
  • Simple
  • Elegancia
  • Eficaz
  • Óptimo
  • Con pruebas

Seguro se pueden agregar más características para un código limpio, un buen código.

Año nuevo

Y bueno ya comenzó otro año, hay tantas cosas por hacer qué uno anda todo norteado. Creo que una de las cosas interesantes este año es que el curso de introducción a la programación con Java, el cuál doy casi cada 6 meses, tiene personas animadas para aprender a programar. Tal vez fue un poco cruel el hacer que la clase sea de 8 a 10 de la mañana pero bueh…

A pesar de que ayer regresé a mis días normales de trabajo, aún me siento fuera de lugar. Será por qué me desvelé mucho en vacaciones y estuve enfermo… whatever…

homer-thinking.png

Pero bueno, este año tengo que retomar mi dominio sobre clojure y retomar todas las cosas que se quedarón pendientes desde diciembre, hay tanto por pensar, qué hasta me dió por escuchar música que no escuchaba desde hace mucho tiempo.

Bueno es hora de comenzar a trabajar en este año nuevo.