Static methods are evil + DateTime

3 visualizações
Pular para a primeira mensagem não lida

Guilherme Oenning

não lida,
17 de ago. de 2009, 09:05:4317/08/2009
para dotnetar...@googlegroups.com

Já li várias vezes que métodos estáticos não são recomendados, alguns dizem que dificulta a testabilidade, outros dizem que seu design fica mais estruturado do que orientada a objeto.

Eu sempre concordei com a segunda afirmação, mas eu nunca tinha entendi o porque do problema com testabilidade.

Ontem eu descobri (pelo menos eu acho) e senti na pele o problema. Quando foi você quem criou o método é fácil voltar atrás e refazer o design, mas e qual não foi você?

Pega o exemplo do DateTime, todo mundo usa DateTime.Now, como vou testar uma método que se baseia na hora atual para fazer alguma determinada operação?

Obs.: Mudar a hora do sistema no SetUp e voltar no TearDown não é uma opção J.

 

Rodrigo Vieira

não lida,
17 de ago. de 2009, 10:03:3317/08/2009
para dotnetar...@googlegroups.com
Um jeito é usar mock pra isso, por exemplo com TypeMock vc pode fazer
algo do tipo (to escrevendo da minha cabeça, pode tá meio errada a
sintaxe)

Isolate.WhenCalled(() => DateTime.Now).WillReturn(myTime);

- Rodrigo



2009/8/17 Guilherme Oenning <guil...@oenning.eti.br>:

Guilherme Oenning

não lida,
17 de ago. de 2009, 10:24:4617/08/2009
para dotnetar...@googlegroups.com
Então, quando iniciei meu projeto eu fui procurar um framework de Mock e
acabei optando pelo Rhino.Mocks.
Eu vi esse TypeMock, vi alguns exemplos inclusive e achei ótimo. O problema
é que é pago e preciso instalar um MSI.

Você sabe se eu consigo fazer isso aí no Rhino ou Moq?

-----Mensagem original-----
De: dotnetar...@googlegroups.com
[mailto:dotnetar...@googlegroups.com] Em nome de Rodrigo Vieira
Enviada em: segunda-feira, 17 de agosto de 2009 11:04
Para: dotnetar...@googlegroups.com
Assunto: [dotnetarchitects] Re: Static methods are evil + DateTime

Luiz Carlos Faria

não lida,
17 de ago. de 2009, 10:41:0517/08/2009
para dotnetar...@googlegroups.com
Uma abordagem que já implementei foi criar ao invéz de usar o DateTime.Now,
usar uma classe minha para obter a hora do sistema. Basicamente ela pegava a
hora do sistema ou obtinha uma hora fixa, em um repositório de configuração
(no meu caso o banco). Para testar bastava setar uma data na configuração
assim simular um workflow de longa duração me permitindo ir e voltar no
tempo.



-----Mensagem original-----
De: dotnetar...@googlegroups.com
[mailto:dotnetar...@googlegroups.com] Em nome de Rodrigo Vieira
Enviada em: segunda-feira, 17 de agosto de 2009 11:04
Para: dotnetar...@googlegroups.com
Assunto: [dotnetarchitects] Re: Static methods are evil + DateTime


higor.cesar

não lida,
17 de ago. de 2009, 14:15:1617/08/2009
para .Net Architects
Fala Galera, teoricamente se você tiver métodos pequenos voce pode
separar o código que obtem a hora do sistema em uma função. Não é
necessário escrever testes unitários para o que você sabe que vai
funcionar, como exemplo uma função que retorna DateTime.Now.

Higor Ramos
www.higorcesar.com

On 17 ago, 11:41, "Luiz Carlos Faria" <luizcarlosfa...@gmail.com>
wrote:
> Uma abordagem que já implementei foi criar ao invéz de usar o DateTime.Now,
> usar uma classe minha para obter a hora do sistema. Basicamente ela pegava a
> hora do sistema ou obtinha uma hora fixa, em um repositório de configuração
> (no meu caso o banco). Para testar bastava setar uma data na configuração
> assim simular um workflow de longa duração me permitindo ir e voltar no
> tempo.
>
> -----Mensagem original-----
> De: dotnetar...@googlegroups.com
> [mailto:dotnetar...@googlegroups.com] Em nome de Rodrigo Vieira
> Enviada em: segunda-feira, 17 de agosto de 2009 11:04
> Para: dotnetar...@googlegroups.com
> Assunto: [dotnetarchitects] Re: Static methods are evil + DateTime
>
> Um jeito é usar mock pra isso, por exemplo com TypeMock vc pode fazer
> algo do tipo (to escrevendo da minha cabeça, pode tá meio errada a
> sintaxe)
>
> Isolate.WhenCalled(() => DateTime.Now).WillReturn(myTime);
>
> - Rodrigo
>
> 2009/8/17 Guilherme Oenning <guilhe...@oenning.eti.br>:

Rodrigo Kumpera

não lida,
17 de ago. de 2009, 14:19:0317/08/2009
para dotnetar...@googlegroups.com
A questão não é testar DateTime.Now, mas poder testar o comportamento
do sistema de acordo com seu resultado.

Por exemplo, verificar se o sistema se comporta direito nas mudanças de horário
de versão ou em anos bissextos. Ou mesmo poder simular o comportamento ao longo
de períodos longos de tempo sem necessariamente ter que esperar todo esse tempo.


2009/8/17 higor.cesar <higo...@gmail.com>

Guilherme Oenning

não lida,
17 de ago. de 2009, 14:56:2817/08/2009
para dotnetar...@googlegroups.com

O que vocês acham disso? Resolve?

http://codepaste.net/2bk8qd

 

O problema é que o uso dela fica mais complexo do que um simples DateTime.Now.

Rodrigo Kumpera

não lida,
17 de ago. de 2009, 15:07:1317/08/2009
para dotnetar...@googlegroups.com
Mais fácil modelar o problema sob a abstração de um relógio.


2009/8/17 Guilherme Oenning <guil...@oenning.eti.br>

André Dias

não lida,
17 de ago. de 2009, 17:53:3817/08/2009
para dotnetar...@googlegroups.com

Galera,

Talvez eu não tinha entendido a questão, mas se o problema é testar algo com uma data ou horário específico, por que não usar o construtor da classe DateTime?

 

Vamos imaginar que eu precise criar um teste que negue o acesso do usuário ao sistema após o dia 01/09/2009. Qual o problema de utilizar o new DateTime(2009, 09, 01)??

 

Isso não resolve?

Abraços

 

--
André Dias
SCJP - SCWCD - MCP - MCTS - CSM
http://blogs.msdn.com/andredias
http://twitter.com/andrediasbr

Fabio Vazquez

não lida,
17 de ago. de 2009, 18:11:4117/08/2009
para dotnetar...@googlegroups.com
Oi Guilherme,

Acho que precisamos sempre olhar métodos estáticos com desconfiança :)

Métodos estáticos são procedurais por natureza; não possuem seams e não
permitem polimofismo. O caso do DateTime.Now ilustra bem isso. Como já foi
sugerido, você pode resolver o seu problema de testabilidade simplesmente
lançando mão de um framework de isolamento (TypeMock, por exemplo). Alguns
frameworks de isolamento, entretanto, não conseguem lidar com métodos
estáticos, então suas opções ficam bem limitadas realmente. Para esses
casos, eu gosto da idéia de criar uma abstração para a hora, como o código
que você mostrou faz. Outra opção seria você criar um seam para o ponto do
seu código que usa o método DateTime.Now. Imagine que o seu código ogirinal
se parece com este:

----------
public class DateComparer
{
public bool IsDateInThePast(DateTime date)
{
return date.CompareTo(DateTime.Today) < 0;
}
}
----------

Criando um seam (neste caso um método que será responsável por recuperar a
data atual):

----------
public class DateComparer
{
public bool IsDateInThePast(DateTime date)
{
return date.CompareTo(GetReferenceDate()) < 0;
}

protected virtual DateTime GetReferenceDate()
{
return DateTime.Today;
}
}
----------

Note que o novo método foi criado como protected e virtual. Isso nos permite
sobrescrever este método em um código de teste, por exemplo. Imagine que
você está agora criando testes unitários para esta classe. Você pode criar
um classe derivada de DateComparer que servirá somente para o propósito de
testes, mas que altera o comportamento do método GetReferenceDate(). Mmais
ou menos assim:

---------
public class DateComparerTestableForSomethingOld : DateComparer
{
protected override DateTime GetReferenceDate()
{
return new DateTime(1500, 04, 22);
}
}
----------

O seu código de teste poderia ficar assim:

----------
[TestMethod]
public void
IsDateInThePast_WhenTheReferenceDateIsVeryOldAndWeCompareItWithToday_ShouldR
eturnFalse()
{
var dateComparer = new DateComparerTestableForSomethingOld();

bool isDateInThePast = dateComparer.IsDateInThePast(DateTime.Today);

Assert.IsFalse(isDateInThePast);
}
----------

Desculpe pelo exemplo tosco. Não consegui pensar em nada mais criativo :)
De qualquer forma, é só mais uma opção. As alternativas mudam dependendo no
nosso cenário. Ela se provou útil em várias situações para mim.

Grande abraço!
------
Fabio

Luiz Carlos Faria

não lida,
17 de ago. de 2009, 19:10:1117/08/2009
para dotnetar...@googlegroups.com

Resolve se você tiver algum factory que durante os testes possa entregar um determinado DateTime contendo um dia/hora conhecido para o cenário.

Luiz Carlos Faria

não lida,
17 de ago. de 2009, 19:15:3617/08/2009
para dotnetar...@googlegroups.com

Olá André,

 

 

O problema é como o sistema obtém a hora.

 

Vou dar um exemplo: Imagine um workflow onde com 15 dias você precisa iniciar diversos outros processos de alta criticidade pois dada uma determinada lei, com se seu workflow passar de 20 dias a empresa pode ser multada.

 

A intenção de um caso de teste é, reproduzir o dia 1, o dia 2, uma intervenção no dia 3 e em seguida caminhar para o dia 15 para saber se os processos foram corretamente inicializados.

 

Neste caso você precisa de alguém para lhe entregar a hora do sistema, que pode ser fake. Nesse ponto você precisa ter um cara que abstraia a origem da data, para que alguém possa entregar datas diferentes de acordo com a utilidade (testes/prod).

Giovanni Bassi

não lida,
17 de ago. de 2009, 21:54:2117/08/2009
para dotnetar...@googlegroups.com
É isso mesmo: método estáticos são meio perigosos. Não atual sobre uma classe, e só estão em classes porque tem que estar em algum lugar... A classe é um mero contexto de chamada, e não uma matriz para um objeto... sem dúvida esse tipo de abordagem leva a código procedural.
Alguém sugeriu que poderíamos usar mock frameworks para isolar a chamada, mas entendo que não é possível. Quando você chama DateTime.Now, você está acessando uma propriedade estática Now da classe DateTime, e não há nada que um outro objeto possa fazer para interceptar esta chamada e retornar outra coisa...
Se realmente precisamos fazer isso, o jeito é abstrair. O chato é que abstrair algo tão simples como o relógio do sistema pode levar a uma complexidade muito alta da aplicação. Vamos abstrair até onde? Eu faria isso diante de uma necessidade muito forte...
É nesse tipo de cenário que uma linguagem dinâmica brilharia... Se você chamar o código a ser testado via IronRuby, por exemplo, acho que você conseguiria testar sem precisar de uma abstração e um Fake. Basta alterar a propriedade Now antes de chamar o método sendo testado. Vale a pena verificar.

[]'s

Giovanni Bassi
Microsoft MVP, MCSD, MCPD, CSM
Arquiteto de software
http://www.giovannibassi.com


2009/8/17 Guilherme Oenning <guil...@oenning.eti.br>

Rodrigo Vieira

não lida,
18 de ago. de 2009, 01:58:2718/08/2009
para dotnetar...@googlegroups.com
2009/8/18 Giovanni Bassi <gig...@gmail.com>:

> Alguém sugeriu que poderíamos usar mock frameworks para isolar a chamada,
> mas entendo que não é possível. Quando você chama DateTime.Now, você está
> acessando uma propriedade estática Now da classe DateTime, e não há nada que
> um outro objeto possa fazer para interceptar esta chamada e retornar outra
> coisa...

..mas funciona. O TypeMock faz.

Marcel Castilho

não lida,
18 de ago. de 2009, 09:21:3518/08/2009
para dotnetar...@googlegroups.com
É verdade! O TypeMock "mocka" praticamente tudo: métodos estáticos, privados, "sealed classes", propriedades, enfim... é muito poderoso!
 
Muita gente não concorda em dar todo esse poder a um desenvolvedor. Conforme discutido em outras threads, uma das maiores vantagens de práticas como o TDD é o design.
 
Usando frameworks como o TypeMock você pode desenvolver de qualquer forma, sem se preocupar com o "design for testability" ou implementar uma única interface. Ainda assim será possível interceptar as chamadas e testar o código que você criou.
 
De certa forma, frameworks como o Rhino.Mocks ou Moq te obrigam a fazer um design melhor.
 
Abraço,
 
Marcel
 
2009/8/18 Rodrigo Vieira <rodr...@gmail.com>

Rodrigo Vieira

não lida,
18 de ago. de 2009, 09:33:3918/08/2009
para dotnetar...@googlegroups.com
Sim, o TypeMock é bem poderoso, mas se alguém tá escrevendo código
novo e precisando fazer muito mock nos testes, é porque precisa dar
uma reavaliada no desenho do sistema, pois um sistema bem desenhado já
é naturalmente testável e bem isolável, não deveria depender de mocks
o tempo todo. Uns fakes injetados aqui e ali já deveriam dar conta de
quase tudo.

Mas por outro lado, pra escrever testes para sistemas legado, em
especial testes de integração, o TypeMock é uma mão na roda.

- Rodrigo

2009/8/18 Marcel Castilho <marcel....@gmail.com>:

Phillip Calçado

não lida,
18 de ago. de 2009, 10:04:0518/08/2009
para dotnetar...@googlegroups.com
Oi,

Há algum tempo escrevi um post sobre este assunto, falando
especificamente de Ruby e das más-práticas possibilitadas pela
flexibilidade da inguagem:

http://fragmental.tw/2008/01/08/please-do-not-break-into/

[]s

2009/8/18 Rodrigo Vieira <rodr...@gmail.com>:
--
Phillip Calçado
http://fragmental.tw
http://www.fragmental.com.br

Marcel Castilho

não lida,
18 de ago. de 2009, 11:31:3318/08/2009
para dotnetar...@googlegroups.com
Com certeza Rodrigo, é um ótimo framework!
 
Nestes casos que você citou (sistemas legados) é imbatível. Desde que não seja utilizado como desculpa para se esquivar de uma refatoração importante.
 
Também concordo que é possível fazer, senão todos, praticamente todos os testes utilizando fakes ou stubs.

Giovanni Bassi

não lida,
19 de ago. de 2009, 11:54:5019/08/2009
para dotnetar...@googlegroups.com
Deve ter umas magias envolvidas. Vou ver o que eles fazem...

[]'s

Giovanni Bassi
Microsoft MVP, MCSD, MCPD, CSM
Arquiteto de software
http://www.giovannibassi.com


2009/8/18 Rodrigo Vieira <rodr...@gmail.com>

Rodrigo Vieira

não lida,
19 de ago. de 2009, 12:00:5519/08/2009
para dotnetar...@googlegroups.com
Qq coisa pergunta pro Roy Osherove no Twitter, ele é o autor da magia... :)



2009/8/19 Giovanni Bassi <gig...@gmail.com>:

Eduardo Miranda

não lida,
19 de ago. de 2009, 21:31:0019/08/2009
para dotnetar...@googlegroups.com
O Roy pode até saber responder, mas ele não está na empresa há muito tempo.
 
O maior responsável pelo Typemock é o CEO da empresa, Eli Lopian, que durante o início era o "relações públicas", função que o Roy faz hoje.
 
Eles utilizam AOP para interceptar e substituir chamadas.
 
Parte da mágica foi publicada como uma API


 
2009/8/19 Rodrigo Vieira <rodr...@gmail.com>

Giovanni Bassi

não lida,
20 de ago. de 2009, 01:52:0520/08/2009
para dotnetar...@googlegroups.com
Hm... pelo que eu entendi, eles fazem weaving no IL, é isso?
Deve ser isso, porque uma chamada estática não tem como ser substituída. Deve dar um trabalho infernal pra fazer isso funcionar direitinho.
De qualquer forma, acho que buscaria outra forma de trabalhar... algo com menos cara de gambiarra...

[]'s

Giovanni Bassi
Microsoft MVP, MCSD, MCPD, CSM
Arquiteto de software
http://www.giovannibassi.com


2009/8/19 Eduardo Miranda <dud...@gmail.com>
Responder a todos
Responder ao autor
Encaminhar
0 nova mensagem