texto suprimir sobreescribir selecciona quitar que medida letra escribo escribir deshacer borra adelante c# unit-testing datetime testing

c# - suprimir - ¿Cuál es una buena manera de sobrescribir DateTime.Now durante la prueba?



suprimir al escribir (10)

Tengo un código (C #) que se basa en la fecha de hoy para calcular correctamente las cosas en el futuro. Si uso la fecha de hoy en la prueba, tengo que repetir el cálculo en la prueba, lo que no me parece correcto. ¿Cuál es la mejor manera de establecer la fecha a un valor conocido dentro de la prueba para que pueda probar que el resultado es un valor conocido?


¿Ha considerado usar la compilación condicional para controlar lo que sucede durante la depuración / implementación?

p.ej

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

En su defecto, si desea exponer la propiedad para poder manipularla, todo esto es parte del desafío de escribir un código comprobable , que es algo en 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, simplemente nadie lo ve externamente.

Sin embargo, la creación y la interfaz pueden parecer mucho trabajo para este ejemplo (por lo que opté por la compilación condicional).


Ayende Rahien uses un método estático que es bastante simple ...

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


Creo que crear una clase de reloj por separado para algo tan simple como obtener la fecha actual es un poco exagerado.

Puede pasar la fecha de hoy como un parámetro para que pueda ingresar una fecha diferente en la prueba. Esto tiene el beneficio adicional de hacer que su código sea más flexible.


La clave para una prueba unitaria exitosa es el desacoplamiento . Debe separar su código interesante de sus dependencias externas, por lo que puede 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 el DateTime a un nuevo método o clase o lo que sea lógico en su caso, y pasar el DateTime. Ahora, su prueba de unidad puede pasar un DateTime arbitrario, para producir resultados predecibles.


Mi preferencia es que las clases que usan 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 probar, como

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

Puede haber una sobrecarga 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 simple inyección de constructor / instalador antiguo o incluso un patrón de puerta de enlace estática )

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 va a introducir dolor en otros niveles.

Además, usar DateTime.Now e incluirlo en tus cálculos no solo no te hace sentir bien: te roba la capacidad de probar momentos específicos, por ejemplo, si descubres 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 siempre que lo desee.


Otro usando Microsoft Moles ( marco de aislamiento para .NET ).

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

Moles permite reemplazar cualquier método .NET con un delegado. Moles admite métodos estáticos o no virtuales. Moles confía en el perfilador de Pex.


Podrías inyectar la clase (mejor: método / delegado ) que usas para DateTime.Now en la clase que se está probando. Haga que DateTime.Now sea ​​un valor predeterminado y solo establezca la prueba en 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 a los delegados para esto, ya que no saturan su jerarquía de clases con cosas como IClock ...



Sugeriría usar un patrón IDisposable:

[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); } }

En detalle se describe aquí: http://www.lesnikowski.com/blog/index.php/testing-datetime-now/


Usar Microsoft Fakes para crear un shim es una forma realmente fácil de hacer esto. Supongamos que tengo la siguiente clase:

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

En Visual Studio 2012 puede agregar un ensamblaje de Fakes a su proyecto de prueba haciendo clic con el botón derecho en el ensamblaje para el que desea crear Fakes / Shims y seleccionando "Add Fakes Assembly".

Finalmente, aquí está cómo se vería la clase de prueba:

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); } } } }