Putting it together

by Luismi Cavallé

Shoulda es un plugin para hacer BDD en Rails. Se trata de una alternativa sencilla a RSpec que, a diferencia de este, no proporciona un framework completo sino una serie de helpers, macros y assertions sobre la librería estándar de testing de Ruby. Así, permite escribir tests especificar comportamientos, por ejemplo, de la siguiente manera:

class PostTest < Test::Unit::TestCase
  load_all_fixtures

  should_belong_to :user
  should_have_many :tags, :through => :taggings

  should_require_unique_attributes :title
  should_require_attributes :body, :message => /wtf/
  should_require_attributes :title
  should_only_allow_numeric_values_for :user_id
end

He robado el ejemplo de la misma página principal del plugin. El caso es que, para satisfacer el test, podríamos escribir una clase parecida a esta:

class Post < ActiveRecord::Base
  belongs_to :user
  has_many :tags, :through => :taggings

  validates_uniqueness_of :title
  validates_presence_of :body, :message => "wtf"
  validates_presence_of :title
  validates_numericability_of :user_id
end

No lo he llegado a testear, pero tiene pinta de que más o menos esa sería la implementación. La cuestión es que no dejo de tener la sensación de que hay algo incorrecto en esta manera de testear. El test se corresponde línea a línea con la implementación. Es lo mismo, pero dicho con otro lenguaje. Sería perfectamente posible generar la clase a partir del test. Con lo que llego a la conclusión de que lo único que hacemos siguiendo este enfoque es escribir dos veces lo mismo, duplicar la información y, por tanto, hacer menos mantenible nuestra aplicación.

Me inquieta especialmente pensar que Shoulda no es más que una refactorización sobre la manera estándar de testear en Rails. Por ejemplo:

class PostTest < Test::Unit::TestCase
  ...
  # Esto:
  should_require_attributes :title

  # Es lo mismo que esto:
  def test_should_require_title
    post = Post.new
    assert !post.valid?
    assert post.errors.on(:title)
  end

De hecho, con un poquito de metaprogramación podríamos facilmente escribir nuestra propia versión del should_require_attributes:

def should_require_attributes(attribute)
  define_method "test_should_require_#{attribute}" do
    klass = self.name.gsub(/Test$/, '').constantize
    object = klass.new
    assert !object.valid?
    assert object.errors.on(attribute)
  end
end

Y he aquí la angustiosa conclusión a la que llego, que me hace despertar con taquicardias por los noches: Si los tests declarativos de Shoulda me parecen incorrectos, y los tests declarativos de Shoulda son solo una refactorización y, por tanto, equivalentes funcionalmente a la manera normal de testear en Rails:

TODO LO DECLARATIVO DE MIS MODELOS NO TIENE SENTIDO SER TESTEADO A NIVEL DE UNIDAD

Dicho queda, en mayúsculas y negrita. A ver si alguien encuentra el error en mi razonamiento, el detalle que se me escapa, desmonta mi teoría, y por fin me devuelve el sosiego, la paz interior y el flow.