Go to English Blog

Testando datas no Ruby

Leia em 1 minuto

Estou reescrevendo o Spesa e, obviamente, ele exige uma série de cálculos com datas devido aos agendamentos. Na versão anterior, a maioria destes cálculos era feita em consultas SQL, trazendo uma complexidade um tanto quanto desnecessária, já que muito da lógica fica no banco de dados, incluindo algumas Stored Procedures.

Na nova versão toda esta lógica foi trazida para o Ruby, o que facilitou o desenvolvimento. Mas nem tudo foi fácil. Os testes exigiram um pouco de pesquisa — não mais que alguns minutos — já que testar datas é sempre um pouco mais complicado. A dificuldade está em manter a consistência, já que o resultado irá variar dependendo do dia que você executar os testes.

Devido a possibilidade de sobrecarga de métodos que o Ruby oferece, é simples e ao mesmo tempo elegante. Utilizando o código abaixo é possível forçar o método Time.now para sempre retornar uma data específica.

class Time
  class << self
    # Time we might be behaving as
    attr_reader :mock_time

    # Set new time to pretend we are.
    def mock_time=(new_now)
      @mock_time = new_now
    end

    # now() with possible augmentation
    def now_with_mock_time
      mock_time || now_without_mock_time
    end
    alias_method_chain :now, :mock_time
  end
end

Adicione o código acima ao seu helper de testes, que no meu caso é "spec/spec_helper.rb", pois estou usando RSpec. Para usá-lo, basta definir uma data como sendo a atual. No exemplo abaixo estou definindo a data atual como sendo "28/02/2007".

it "should mock today method" do
  mocked_today = Time.gm(2007, 2, 28)
  Time.mock_time = mocked_today

  mydate = Date.today
  mydate.should eql(mocked_today.to_date)
end

Vi essa dica no Ruby Forum e com certeza vale o bookmark.

Update: Seguindo a dica do Tapajós, você pode usar o Mocka, mas de uma maneira diferente da explicada por ele:

Time.stubs(:now).returns(Time.gm(2008, 2, 1))

Update 2: Mais simples ainda e usando apenas o próprio RSpec:

Time.stub!(:now).and_return(Time.gm(2008, 2, 1))