Pergunta

Eu tenho alguns (C#) código que se baseia na data de hoje para calcular corretamente as coisas no futuro.Se eu usar a data de hoje, no ensaio, eu tenho que repetir o cálculo do teste, que não se sente bem.Qual é a melhor maneira de definir a data para um valor conhecido dentro do teste, para que eu possa testar o resultado é um valor conhecido?

Foi útil?

Solução

A minha preferência é ter classes que usam o tempo, na verdade, dependem de uma interface, tais como

interface IClock
{
    DateTime Now { get; } 
}

Com uma implementação concreta

class SystemClock: IClock
{
     DateTime Now { get { return DateTime.Now; } }
}

Então, se você quiser, você pode fornecer qualquer outro tipo de relógio que você deseja para o teste, como

class StaticClock: IClock
{
     DateTime Now { get { return new DateTime(2008, 09, 3, 9, 6, 13); } }
}

Pode haver alguma sobrecarga na prestação de relógio para a classe que depende de ti, mas que pode ser manipulada por qualquer número de injeção de dependência soluções (usando uma Inversão de Controle de recipiente, velho e simples do construtor/montador de injeção, ou até mesmo um Estática Gateway Padrão).

Outros mecanismos de entrega de um objecto ou método que fornece desejado vezes também funciona, mas eu acho que o fundamental é evitar repor o relógio do sistema, que vai apresentar dor em outros níveis.

Além disso, usando DateTime.Now e incluindo-a em seus cálculos, não apenas não se sente bem - ele rouba-lhe a capacidade para testar determinados momentos, por exemplo, se você descobrir um erro que só acontece perto de um limite de meia-noite, ou às terças-feiras.Usando o tempo atual não permitem que você teste os cenários.Ou pelo menos não sempre que você quiser.

Outras dicas

Ayende Rahien usa um método estático que é bastante simples...

public static class SystemTime
{
    public static Func<DateTime> Now = () => DateTime.Now;
}

Eu acho que criar um separar classe de relógio para algo simples, como ter a data atual é um pouco exagero.

Você pode passar a data de hoje como um parâmetro, de forma que você pode inserir uma data diferente no teste.Isso tem o benefício adicional de tornar o código mais flexível.

Usando o Microsoft Fakes para criar um calço é realmente uma maneira fácil de fazer isso.Suponha que eu tinha a seguinte classe:

public class MyClass
{
    public string WhatsTheTime()
    {
        return DateTime.Now.ToString();
    }

}

No Visual Studio 2012, você pode adicionar uma Falsificação da assembleia para o seu projeto de teste com o botão direito sobre a montagem que você deseja criar Fakes/Correções para o e selecionando "Adicionar Fakes Assembly"

Adding Fakes Assembly

Finalmente, Aqui está o que a classe de teste seria:

using System;
using ConsoleApplication11;
using Microsoft.QualityTools.Testing.Fakes;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace DateTimeTest
{
[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void TestWhatsTheTime()
    {

        using(ShimsContext.Create()){

            //Arrange
            System.Fakes.ShimDateTime.NowGet =
            () =>
            { return new DateTime(2010, 1, 1); };

            var myClass = new MyClass();

            //Act
            var timeString = myClass.WhatsTheTime();

            //Assert
            Assert.AreEqual("1/1/2010 12:00:00 AM",timeString);

        }
    }
}
}

A chave para o sucesso de teste de unidade é dissociação.É preciso separar o código interessante a partir de suas dependências externas, de modo que ele pode ser testado isoladamente.(Felizmente, Test-Driven Development produz dissociado de código.)

Neste caso, o externo é a de data / hora actual.

Meu conselho aqui é extrair a lógica que lida com a data e hora de um novo método ou classe ou o que faz sentido no seu caso, e passar a data e hora em.Agora, o teste de unidade pode passar um arbitrário DateTime, para produzir resultados previsíveis.

Outra, usando o Microsoft Moles (Estrutura de isolamento para .NET).

MDateTime.NowGet = () => new DateTime(2000, 1, 1);

Moles permite substituir qualquer .NET método com um delegado.As toupeiras suporta estático ou não de métodos virtuais.As toupeiras depende do profiler do Pex.

Eu gostaria de sugerir o uso de IDisposable padrão:

[Test] 
public void CreateName_AddsCurrentTimeAtEnd() 
{
    using (Clock.NowIs(new DateTime(2010, 12, 31, 23, 59, 00)))
    {
        string name = new ReportNameService().CreateName(...);
        Assert.AreEqual("name 2010-12-31 23:59:00", name);
    } 
}

Em detalhes descritos aqui:http://www.lesnikowski.com/blog/index.php/testing-datetime-now/

Resposta simples:Sistema de valas.Data / hora :) em Vez disso, use NodaTime e é biblioteca de testes: NodaTime.Teste.

Leitura adicional:

Você pode injetar a classe (melhor:método/delegado que você usa para DateTime.Now na classe que está sendo testada.Tem DateTime.Now ser um valor padrão e defina-a apenas em testes de um manequim método que retorna um valor constante.

EDITAR: O que Blair Conrad disse (ele tem algum código para olhar).Exceto, eu tendem a preferir os delegados para isso, como elas não ocuparem a sua hierarquia de classes com coisas como IClock...

Eu enfrentei essa situação tantas vezes, que eu criados simples nuget que expõe Agora propriedade através da interface.

public interface IDateTimeTools
{
    DateTime Now { get; }
}

A implementação é muito simples

public class DateTimeTools : IDateTimeTools
{
    public DateTime Now => DateTime.Now;
}

Então, depois de adicionar o nuget para o meu projeto, eu posso usá-lo na unidade de testes

enter image description here

Você pode instalar o módulo de direito da interface de usuário do Gerenciador de Pacotes Nuget ou usando o comando:

Install-Package -Id DateTimePT -ProjectName Project

E o código para o Nuget é aqui.

O exemplo de uso com o Autofac pode ser encontrado aqui.

Você já pensou em usar a compilação condicional para controlar o que acontece durante a depuração/implementação?

exemplo:

DateTime date;
#if DEBUG
  date = new DateTime(2008, 09, 04);
#else
  date = DateTime.Now;
#endif

Se isso falhar, você quer expor a propriedade para que você possa manipulá-lo, tudo isso é parte do desafio de escrever testáveis o código, que é algo que eu estou atualmente wrestling mim :D

Editar

Uma grande parte de mim teria preferência Blair abordagem.Isso permite que você para de "hot plug" de partes do código para auxílio no teste.Tudo segue o princípio do design encapsular o que varia código de teste não é diferente para a produção de código, só que ninguém nunca vê-lo externamente.

A criação e a interface pode parecer um monte de trabalho para esse exemplo que (é por isso optei por compilação condicional).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top