Testando datas no Ruby
03/02/08
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))
- Permalink
- Trackback
- Comentários (5)
- Ao som de: Jack Johnson – Constellations
Textos escritos por
Comentários #
Nando, realmente testes com datas dão trabalho. Já tivemos problemas com testes de datas mas no nosso caso usamos o recurso de compor nossas fixtures de forma dinâmica. Exemplos:
data_agendamento:
Isso resolveu nosso dilema que um dia as fixures serão passado. :-)
No seu caso o que me chamou a atenção é que penso que você poderia resolver isso com o uso de mocks. Algo como:
Time.expects(:now).returns(sua_data)
Existe alguma razão para não usar dessa forma ?
Um abração
O meu exemplo usando o mocha supõe que no seu RSpec seja o mocha, se não for (for o padrão do SpecOnRails) tem que ser diferente.
Um abração
Fala Tapajós! O único motivo para eu não fazer isso usando algo como você passou é o comodismo. :)
Estava com pressa para fazer esse negócio funcionar, e como ainda estou começando com RSpec, usei esse mesmo!
Eu tinha colocado no taskpaper do projeto um item pendente pra converter isso! Agora não vou precisar mais procurar, pois você já passou a solução mastigada! :)
Oi, Nando.
Só para complementar, na sintaxe do RSpec, o mock seria algo como o exemplo abaixo:
Time.should_receive(:now).at_least(:once).and_return(Time.gm(2007, 2, 2008))
O .at_least(:once) é algo que possivelmente será necessário para funcionar neste caso específico. No teste que fiz aqui, o .now acaba sendo chamado 4 vezes.
Grande abraço, Vinícius.
Fala Teles!
Eu fiz assim: Time.stub!(:now).and_return(Time.gm(2008, 2, 1)), usando o próprio RSpec. Mais simples ainda! :)
Deixe um comentário