Usando Mock Objects en Rails

El ejemplo frecuente que se utiliza para mostrar el beneficio de usar Mock Objects durante el testing de nuestro software, es el caso en el que la aplicación que estamos desarrollando depende de un ente externo que no está disponible durante la fase de desarrollo/testing, por ejemplo nuestro software debe interactuar con un ERP (SAP, Baan, etc), y no tenemos el ERP a nuestra disposición. En este caso lo que se hace es crear un mock object que reemplaza el ERP (a momento de testing) , para poder probar asi la funcionalidad de nuestro software.

A pesar de que el ejemplo "frecuente" es válido, hay situaciones más comúnes que tambien justifican el uso de mock objects.

Supongamos que nuestro software es una aplicación Web (hecha en RubyOnRails) que tiene la funcionalidad de autenticar un usuario vía un cookie (remember-me feature), y que dicho cookie tiene una fecha de expiración, despues de la cual no es válido para autenticar un usuario en nuestra aplicación.

Dado esto se quiere probar que la expiración del cookie efectivamente funcione (del lado de nuestra aplicación, no del lado del browser). Es decir, tenemos que simular el paso del tiempo (para alcanzar que la fecha de expiración)

Para este functional test tenemos los siguientes archivos: /test/functional/user_controller_test.rb (suponiendo que el controller encargado de la autenticación es el user_controller.rb) y /test/mocks/test/timer.rb

Nuestro /test/functional/user_controller_test.rb sería algo de este estilo:

require File.dirname(__FILE__) + '/../test_helper'
require 'user_controller'

# Re-raise errors caught by the controller.
class UserController; def rescue_action(e) raise e end; end

class UserControllerTest <Test::Unit::TestCase
  fixtures :users

  def setup
    @controller = UserController.new
    @request    = ActionController::TestRequest.new
    @response   = ActionController::TestResponse.new
  end

  def test_login_cookie_expired
    # go to a secure page with a valid value cookie but already expired
    @request.cookies['remember'] = CGI::Cookie.new('remember', 'valid_value')
    Time.advance_by_days = APP_CONFIG[:cookie_login_expires]
    get :secure_page
    assert_redirected_to :action => 'login'
    Time.advance_by_days = 0
  end
end

El final del método test_login_cookie_expired es el que hace la prueba de la expiración del cookie, para esto lo que hacemos es modificar la clase Time, en particular el método now y le agregamos el atributo advance_by_days a la clase (no a las instancias), vía un mock object (/test/mocks/test/timer.rb) para lograr simular el "avance del tiempo".

require 'time'

Time.class_eval {
  @@advance_by_days = 0
  cattr_accessor :advance_by_days

  class << Time
    #> Este alias permite conservar el método original
    alias now_old now
    #> Aquí se redefine el método "now"
    def now
      if Time.advance_by_days != 0
        return Time.at(now_old.to_i + Time.advance_by_days * 60 * 60 * 24 + 1)
      else
        now_old
      end
    end
  end
}

El archivo /test/mocks/test/timer.rb sería:

Esta modificación a la clase Time funciona (y en general los mock objects), porque Rails modifica el search path para incluir de primero el path de los mocks ( /test/mocks/test ).

La idea de modificar la clase Time fue tomada del SaltedHashLoginGenerator

"A language that doesn't affect the way you think about programming, is not worth knowing."

Alan J. Perlis


0 Respuestas a “Usando Mock Objects en Rails”

  1. Ningún Comentario

Añade un Comentario





RSS feeds

Suscríbete a nuestros RSS Feeds