Eu tenho alguns testes de unidade que esperam que o 'horário atual' seja diferente do DateTime. Agora, e não quero alterar o horário do computador, obviamente.
Qual é a melhor estratégia para conseguir isso?
c#
unit-testing
datetime
mstest
systemtime
Pedro
fonte
fonte
DateTime
. Você testaria esse método; o método existente simplesmente chamaria a sobrecarganow
.Respostas:
A melhor estratégia é agrupar o horário atual em uma abstração e injetar essa abstração no consumidor .
Como alternativa , você também pode definir uma abstração de tempo como um Contexto Ambiental :
Isso permitirá que você o consuma assim:
Em um teste de unidade, você pode substituir
TimeProvider.Current
por um objeto Testar Duplo / Simulado. Exemplo usando Moq:No entanto, ao testar a unidade com estado estático, lembre-se sempre de desmontar o equipamento ligando
TimeProvider.ResetToDefault()
.fonte
DateTime.UtcNow
e similares, mas as revisões de código são uma boa ideia.Estas são todas boas respostas, foi o que fiz em um projeto diferente:
Uso:
Obter a data REAL de hoje Hora
Em vez de usar DateTime.Now, você precisa usar
SystemTime.Now()
... Não é uma mudança difícil, mas esta solução pode não ser ideal para todos os projetos.Viagem no tempo (vamos passar 5 anos no futuro)
Get Our Fake "today" (daqui a 5 anos a partir de 'today')
Redefinir a data
fonte
Moles:
Isenção de responsabilidade - trabalho na Moles
fonte
Você tem algumas opções para fazer isso:
Use a estrutura de simulação e use um DateTimeService (implemente uma classe de wrapper pequena e injete-a no código de produção). A implementação do wrapper acessará o DateTime e nos testes você poderá zombar da classe do wrapper.
Use o Typemock Isolator , ele pode falsificar o DateTime.Now e não exigirá que você altere o código em teste.
Use Moles , também pode falsificar DateTime. e não exigirá alterações no código de produção.
Alguns exemplos:
Classe Wrapper usando Moq:
Falsificando DateTime diretamente usando o Isolator:
Isenção de responsabilidade - trabalho na Typemock
fonte
Adicione uma montagem falsa para o sistema (clique com o botão direito do mouse em Referência do sistema => Adicionar montagem falsa).
E escreva no seu método de teste:
fonte
Em relação à resposta @crabcrusherclamcollector, há um problema ao usar essa abordagem nas consultas EF (System.NotSupportedException: o tipo de nó de expressão LINQ 'Invoke' não é suportado no LINQ to Entities). Eu modifiquei a implementação para isso:
fonte
Para testar um código que depende do
System.DateTime
, osystem.dll
deve ser ridicularizado.Existem duas estruturas que eu conheço que fazem isso. Falsificações e blusas da Microsoft .
As falsificações da Microsoft exigem um ultimato do visual studio 2012 e funcionam diretamente do compton.
O Smocks é um código aberto e muito fácil de usar. Pode ser baixado usando o NuGet.
O seguinte mostra uma simulação de
System.DateTime
:fonte
Um
SystemClock
uso seguro de threadsThreadLocal<T>
funciona muito bem para mim.ThreadLocal<T>
está disponível no .Net Framework v4.0 e superior.Exemplo de uso:
fonte
Now
instância atual .AsyncLocal
, em vez deThreadLocal
Console.WriteLine(SystemClock.Now); SystemClock.Set(new DateTime(2017, 01, 01)); Console.WriteLine(SystemClock.Now); await Task.Delay(1000); Console.WriteLine(SystemClock.Now);
Encontrei esse mesmo problema, mas encontrei um projeto de pesquisa da Microsoft que resolve esse problema.
http://research.microsoft.com/en-us/projects/moles/
Moles é uma estrutura leve para stubs de teste e desvios no .NET que se baseia em delegados. Moles podem ser usados para desviar qualquer método .NET, incluindo métodos não virtuais / estáticos em tipos fechados
O código de amostra foi modificado a partir do original.
Eu tinha feito o que outros sugeriam e abstraído o DateTime em um provedor. Pareceu errado e eu senti que era demais apenas para testar. Vou implementar isso no meu projeto pessoal esta noite.
fonte
Uma nota especial sobre zombaria
DateTime.Now
com o TypeMock ...O valor de
DateTime.Now
deve ser colocado em uma variável para que isso seja zombado corretamente. Por exemplo:Isso não funciona:
No entanto, isso faz:
fonte
Zombar de objetos.
Um DateTime simulado que retorna um Agora apropriado para o seu teste.
fonte
Estou surpreso que ninguém tenha sugerido uma das maneiras mais óbvias a seguir:
Em seguida, você pode simplesmente substituir esse método em seu teste duas vezes.
Eu também gosto de injetar uma
TimeProvider
classe em alguns casos, mas para outros, isso é mais do que suficiente. Eu provavelmente prefeririaTimeProvider
versão se você precisar reutilizá-la em várias classes.EDIT: Para qualquer pessoa interessada, isso é chamado de adição de "costura" à sua classe, um ponto em que você pode se conectar ao seu comportamento para modificá-lo (para fins de teste ou não) sem realmente precisar alterar o código na classe.
fonte
A boa prática é quando DateTimeProvider implementa IDisposable.
Em seguida, você pode injetar seu DateTime falso para testes de unidade
Veja o exemplo Exemplo de DateTimeProvider
fonte
Uma maneira limpa de fazer isso é injetar o VirtualTime. Permite controlar o tempo. Primeiro instale o VirtualTime
Isso permite, por exemplo, acelerar o tempo 5 vezes mais em todas as chamadas para DateTime.Now ou UtcNow
Para tornar o tempo mais lento, por exemplo, 5 vezes mais lento,
Para fazer o tempo parar, faça
Retroceder no tempo ainda não foi testado
Aqui está um exemplo de teste:
Você pode conferir mais testes aqui:
https://github.com/VirtualTime/VirtualTime/blob/master/VirtualTimeLib.Tests/when_virtual_time_is_used.cs
O que a extensão DateTime.Now.ToVirtualTime oferece é uma instância do ITime que você passa para um método / classe que depende do ITime. some DateTime.Now.ToVirtualTime está configurado no contêiner DI de sua escolha
Aqui está outro exemplo de injeção em um controlador de classe
fonte
Estávamos usando um objeto estático SystemTime, mas tivemos problemas ao executar testes de unidade paralelos. Tentei usar a solução de Henk van Boeijen, mas tive problemas em threads assíncronos gerados, e acabei usando o AsyncLocal de uma maneira semelhante a esta abaixo:
Fonte: https://gist.github.com/CraftyFella/42f459f7687b0b8b268fc311e6b4af08
fonte
Uma pergunta antiga, mas ainda válida.
Minha abordagem é criar uma nova interface e classe para encerrar a
System.DateTime.Now
chamadaEssa interface pode ser injetada em qualquer classe que precise obter a data e hora atuais. Neste exemplo, tenho uma classe que adiciona um intervalo de tempo à data e hora atuais (uma unidade testável System.DateTime.Now.Add (TimeSpan))
E testes podem ser escritos para garantir que funcionem corretamente. Estou usando NUnit e Moq, mas qualquer estrutura de teste fará
Esse padrão vai funcionar para todas as funções que dependem de fatores externos, como
System.Random.Next()
,System.DateTime.Now.UtcNow
,System.Guid.NewGuid()
etc.Consulte https://loadlimited.visualstudio.com/Stamina/_git/Stamina.Core para obter mais exemplos ou obtenha o https://www.nuget.org/packages/Stamina.Core nuget package.
fonte
Você pode alterar a classe que está testando para usar uma
Func<DateTime>
que passará pelos parâmetros do construtor; portanto, quando você cria uma instância da classe em código real, pode passar() => DateTime.UtcNow
para oFunc<DateTime>
parâmetro e, no teste, pode passar o tempo em que deseja testar.Por exemplo:
fonte
Eu tive o mesmo problema, mas estava pensando que não deveríamos usar as coisas definidas de data e hora na mesma classe. porque poderia levar ao mau uso um dia. então eu usei o provedor como
Para testes, no projeto de teste fez um ajudante que lidará com coisas definidas,
no código
e em testes
Mas é preciso lembrar de uma coisa: em algum momento o DateTime real e o DateTime do provedor não funcionam da mesma maneira
Presumi que a deferência seria máxima TimeSpan.FromMilliseconds (0.00002) . Mas na maioria das vezes é ainda menos
Encontre a amostra em MockSamples
fonte
Aqui está a minha resposta para esta pergunta. Combino o padrão 'Contexto ambiente' com IDisposable. Portanto, você pode usar o DateTimeProvider.Current no código normal do programa e, no teste, substitui o escopo por uma instrução using.
Aqui está como usar o DateTimeProvider acima em um teste de unidade
fonte
Usando
ITimeProvider
isso, fomos forçados a levá-lo para um projeto comum compartilhado especial que deve ser referenciado pelo restante de outros projetos. Mas isso complicou o controle de dependências .Procuramos
ITimeProvider
no framework .NET. Pesquisamos o pacote NuGet e encontramos um que não funcionaDateTimeOffset
.Por isso, criamos nossa própria solução, que depende apenas dos tipos da biblioteca padrão. Estamos usando uma instância de
Func<DateTimeOffset>
.Como usar
Como registrar
Autofac
( Para futuros editores: anexe seus casos aqui ).
Como teste de unidade
fonte
Talvez uma solução menos profissional, mas mais simples, possa ser tornar um parâmetro DateTime no método do consumidor. Por exemplo, em vez de fazer um método como SampleMethod, faça SampleMethod1 com o parâmetro. O teste de SampleMethod1 é mais fácil
fonte