Mi preferencia es que las clases que usen el tiempo realmente dependan de una interfaz, como
interface IClock
{
DateTime Now { get; }
}
Con una implementación concreta
class SystemClock: IClock
{
DateTime Now { get { return DateTime.Now; } }
}
Luego, si lo desea, puede proporcionar cualquier otro tipo de reloj que desee para la prueba, como
class StaticClock: IClock
{
DateTime Now { get { return new DateTime(2008, 09, 3, 9, 6, 13); } }
}
Puede haber algunos gastos generales al proporcionar el reloj a la clase que depende de él, pero eso podría ser manejado por cualquier cantidad de soluciones de inyección de dependencia (usando un contenedor de Inversión de Control, una inyección simple de constructor / configurador antiguo o incluso un Patrón de Puerta de Enlace Estático ).
Otros mecanismos de entrega de un objeto o método que proporciona los tiempos deseados también funcionan, pero creo que la clave es evitar reiniciar el reloj del sistema, ya que eso solo introducirá dolor en otros niveles.
Además, usarlo DateTime.Now
e incluirlo en sus cálculos no solo no se siente bien, sino que le priva de la capacidad de probar momentos particulares, por ejemplo, si descubre un error que solo ocurre cerca de un límite de medianoche o los martes. Usar la hora actual no le permitirá probar esos escenarios. O al menos no cuando quieras.
UtcNow
debe usar y luego ajustar adecuadamente de acuerdo con la preocupación del código, por ejemplo, lógica de negocios, interfaz de usuario, etc. La manipulación de la fecha y hora en las zonas horarias es un campo minado, pero el mejor primer paso hacia adelante es comenzar siempre con Hora UTC.Ayende Rahien utiliza un método estático que es bastante simple ...
fuente
Creo que crear una clase de reloj separada para algo simple como obtener la fecha actual es un poco exagerado.
Puede pasar la fecha de hoy como parámetro para poder ingresar una fecha diferente en la prueba. Esto tiene el beneficio adicional de hacer que su código sea más flexible.
fuente
Usar Microsoft Fakes para crear una corrección es una forma realmente fácil de hacer esto. Supongamos que tuviera la siguiente clase:
En Visual Studio 2012, puede agregar un ensamblaje de falsificaciones a su proyecto de prueba haciendo clic con el botón derecho en el ensamblaje para el que desea crear falsificaciones / calzas y seleccionando "Agregar ensamblaje de falsificaciones".
Finalmente, así es como se vería la clase de prueba:
fuente
La clave para una prueba unitaria exitosa es el desacoplamiento . Debe separar su código interesante de sus dependencias externas, para que pueda probarse de forma aislada. (Afortunadamente, el desarrollo basado en pruebas produce código desacoplado).
En este caso, su externo es el DateTime actual.
Mi consejo aquí es extraer la lógica que trata con DateTime a un nuevo método o clase o lo que tenga sentido en su caso, y pasar el DateTime. Ahora, su prueba unitaria puede pasar un DateTime arbitrario, para producir resultados predecibles.
fuente
Otro que usa Microsoft Moles ( marco de aislamiento para .NET ).
fuente
Sugeriría usar el patrón IDisposable:
En detalle se describe aquí: http://www.lesnikowski.com/blog/index.php/testing-datetime-now/
fuente
Respuesta simple: deshazte de System.DateTime :) En su lugar, usa NodaTime y su biblioteca de prueba: NodaTime.Testing .
Otras lecturas:
fuente
Puede inyectar la clase (mejor: método / delegado ) que usa
DateTime.Now
en la clase que se está probando. DebeDateTime.Now
ser un valor predeterminado y solo configúrelo en las pruebas con un método ficticio que devuelva un valor constante.EDITAR: Lo que dijo Blair Conrad (tiene un código para mirar). Excepto, tiendo a preferir delegados para esto, ya que no abarrotan su jerarquía de clases con cosas como
IClock
...fuente
Me enfrenté a esta situación con tanta frecuencia que creé un nuget simple que expone la propiedad Now a través de la interfaz.
La implementación es, por supuesto, muy sencilla
Entonces, después de agregar nuget a mi proyecto, puedo usarlo en las pruebas unitarias
Puede instalar el módulo directamente desde la GUI Nuget Package Manager o usando el comando:
Y el código del Nuget está aquí .
El ejemplo de uso con Autofac se puede encontrar aquí .
fuente
¿Ha considerado usar la compilación condicional para controlar lo que sucede durante la depuración / implementación?
p.ej
De lo contrario, desea exponer la propiedad para poder manipularla, todo esto es parte del desafío de escribir código comprobable , que es algo con lo que actualmente estoy luchando: D
Editar
Una gran parte de mí preferiría el enfoque de Blair . Esto le permite "conectar en caliente" partes del código para ayudar en las pruebas. Todo sigue el principio de diseño que encapsula lo que varía el código de prueba no es diferente al código de producción, es solo que nadie lo ve externamente.
Sin embargo, la creación de una interfaz puede parecer mucho trabajo para este ejemplo (razón por la cual opté por la compilación condicional).
fuente
DateTime
'sNow
yToday
etc