Mock de Timer em Moq

2024-07-17 computer blog

Acabou o dia e ainda não consegui terminar este mock dos infernos. Estou tentando criar o primeiro exemplo que usa dois wrappers nunca antes *mockados* (incluindo um timer), mas ficou faltando mockar o System.Timer. O teste já funciona, mas ele é obrigado a dar um *sleep* pra esperar o timer terminar.

O método que precisa ser testado é chamado como trigger deste timer. O teste constroi o objeto e chama um outro método de início que inicia o timer. O teste é quando o timer dispara a chamada do *callback*.

class A {
  init() {
    timer.init(callback);
  }
  callback() {
    // code to be tested
  }
}

Test() {
A a;
a.init();
}

Meu objetivo era que na chamada de timer.init() ele já rode o callback.

Mockar o timer foi a primeira ideia que me deram no stack overflow. Podem haver outras ideias. O cara do Stack Overflow falou que o timer do dotnet não dá essa flexibilidade, então teria que criar um adapter.

Daí eu achei um wrapper já no código e tentei modificá-lo, mas o evento contido no timer original não é acessível nem chamável e é preciso mexer nos paranauê do wrapper ou achar outro caminho.

Uma ideia que meu amigo Fábio deu foi de um timer genérico a implementar o comportamento em cima dele, mas isso eu já tenho no codebase. O wrapper pode ser usado para isso (ou uma cópia dele). Eu parei justamente nessa parte, mas escrever o código com a biblioteca Moq é que é o parto. Seus exemplos são para usos comuns e aparentemente mockar um timer não é um uso comum.

Esses resultados da internet me fazem pensar que estou indo no caminho errado (se não é comum, tá errado).

  • Fabio: "Se ele é um wrapper por que você não faz a classe crua do zero onde fica tudo sequencial e passa ele para a classe final."
  • Caloni: "Boa!"

Você, caro urso, me deu uma ideia: simplificar a interface de start do TimeWrapper (esse é o nome da classe no basecode) e usar o resultado como uma implementação dummy, mas acho que já pensei num jeito de resolver que é ligeiramente diferente: ao final da tarefa ele chama um timer.Restart e daí é só ir acumulando estado.

Fiz desse jeito, que ficou bem tosco para falar a verdade, mas só de comentar na issue o TL já deu o toque pra funcionar direito. Tem que usar esta construção bizarra:

_timer.Raise(m => m.Elapsed += null, new EventArgs() as ElapsedEventArgs);

Com isso não precisa construir nenhuma classe e disparar o mock do TimerWrapper quando quiser.

A variável _timer no caso é um Mock<TimerWrapper> e o método Raise pode ser usado para disparar eventos. Isso resolve esta saga.

A próxima seria entender o que é necessário para que o Visual Studio pare de reclamar que new EventArgs() as ElapsedEventArgs pode ser null. Bom, ele pode mesmo. Porém, não tem como construir um ElapsedEventArgs sozinho no código. Ideias?

[comment] [Dividindo o assembly para conquistar]