Tengo algunas pruebas unitarias que esperan que la "hora actual" sea diferente a DateTime. Ahora y obviamente no quiero cambiar la hora de la computadora.
¿Cuál es la mejor estrategia para lograr esto?
c#
unit-testing
datetime
mstest
systemtime
Pedro
fuente
fuente
DateTime
. Esta sería la que probaría, el método existente simplemente llamaría a la sobrecarganow
.Respuestas:
La mejor estrategia es envolver el tiempo actual en una abstracción e inyectar esa abstracción en el consumidor .
Alternativamente , también puede definir una abstracción de tiempo como un contexto ambiental :
Esto te permitirá consumirlo así:
En una prueba unitaria, puede reemplazar
TimeProvider.Current
con un objeto Prueba doble / simulacro. Ejemplo usando Moq:Sin embargo, cuando realice pruebas unitarias con estado estático, recuerde siempre derribar su dispositivo llamando
TimeProvider.ResetToDefault()
.fuente
DateTime.UtcNow
y similares, pero las revisiones de código son una buena idea de todos modos.Estas son todas buenas respuestas, esto es lo que hice en un proyecto diferente:
Uso:
Obtenga la fecha y hora REAL de hoy
En lugar de usar DateTime.Now, debes usar
SystemTime.Now()
. Ahora ... No es un cambio difícil, pero esta solución puede no ser ideal para todos los proyectos.Viaje en el tiempo (Vayamos 5 años en el futuro)
Obtenga nuestro falso "hoy" (serán 5 años a partir de 'hoy')
Restablecer la fecha
fuente
Lunares:
Descargo de responsabilidad: trabajo en Moles
fuente
Tienes algunas opciones para hacerlo:
Use el marco de simulación y use un DateTimeService (implemente una clase de contenedor pequeña e inyéctela al código de producción). La implementación del contenedor accederá a DateTime y en las pruebas podrás burlarte de la clase del contenedor.
Use el aislador Typemock , puede simular DateTime.Ahora y no requerirá que cambie el código bajo prueba.
Use Moles , también puede falsificar DateTime.Ahora y no requerirá cambios en el código de producción.
Algunos ejemplos:
Clase de contenedor utilizando Moq:
Falsificando DateTime directamente usando el aislador:
Descargo de responsabilidad: trabajo en Typemock
fuente
Agregue un ensamblaje falso para el sistema (haga clic con el botón derecho en Referencia del sistema => Agregar ensamblaje falso).
Y escribe en tu método de prueba:
fuente
Con respecto a la respuesta @crabcrusherclamcollector, existe un problema al usar ese enfoque en las consultas EF (System.NotSupportedException: el tipo de nodo de expresión LINQ 'Invoke' no es compatible en LINQ to Entities). Modifiqué la implementación a eso:
fuente
Para probar un código que depende de
System.DateTime
, elsystem.dll
debe ser burlado.Hay dos marcos que sé que hacen esto. Microsoft falsificaciones y batas .
Las falsificaciones de Microsoft requieren el ultimátum de Visual Studio 2012 y funcionan directamente desde el compton.
Smocks es de código abierto y muy fácil de usar. Se puede descargar usando NuGet.
A continuación se muestra una simulación de
System.DateTime
:fuente
Un
SystemClock
uso seguro de hilosThreadLocal<T>
funciona muy bien para mí.ThreadLocal<T>
está disponible en .Net Framework v4.0 y superior.Ejemplo de uso:
fuente
Now
instancia actual .AsyncLocal
lugar de usarloThreadLocal
Console.WriteLine(SystemClock.Now); SystemClock.Set(new DateTime(2017, 01, 01)); Console.WriteLine(SystemClock.Now); await Task.Delay(1000); Console.WriteLine(SystemClock.Now);
Me encontré con este mismo problema, pero encontré un proyecto de investigación de Microsoft que resuelve este problema.
http://research.microsoft.com/en-us/projects/moles/
Moles es un marco liviano para trozos de prueba y desvíos en .NET que se basa en delegados. Los lunares se pueden usar para desviar cualquier método .NET, incluidos los métodos no virtuales / estáticos en tipos sellados
El código de muestra fue modificado del original.
Había hecho lo que otros sugerían y resumí DateTime en un proveedor. Simplemente me sentí mal y sentí que era demasiado para probar. Voy a implementar esto en mi proyecto personal esta noche.
fuente
Una nota especial sobre burlarse
DateTime.Now
con TypeMock ...El valor de
DateTime.Now
debe colocarse en una variable para que esto se burle correctamente. Por ejemplo:Esto no funciona:
Sin embargo, esto hace:
fuente
Simulacros de objetos.
Un DateTime simulado que devuelve un Now apropiado para su prueba.
fuente
Me sorprende que nadie haya sugerido una de las formas más obvias de hacerlo:
Luego, simplemente puede anular este método en su prueba doble.
También me gusta inyectar una
TimeProvider
clase en algunos casos, pero para otros, esto es más que suficiente. Probablemente favorecería elTimeProvider
embargo versión si necesita reutilizar esto en varias clases.EDITAR: Para cualquier persona interesada, esto se llama agregar una "costura" a su clase, un punto donde puede engancharse a su comportamiento para modificarlo (con fines de prueba o de otro modo) sin tener que cambiar el código en la clase.
fuente
Una buena práctica es cuando DateTimeProvider implementa IDisposable.
A continuación, puede inyectar su DateTime falso para pruebas unitarias
Ver ejemplo Ejemplo de DateTimeProvider
fuente
Una forma limpia de hacer esto es inyectar VirtualTime. Te permite controlar el tiempo. Primero instala VirtualTime
Eso permite, por ejemplo, hacer que el tiempo se mueva 5 veces más rápido en todas las llamadas a DateTime.Now o UtcNow
Para hacer que el tiempo se mueva más lento, por ejemplo, 5 veces más lento
Para hacer que el tiempo se detenga
Retroceder en el tiempo aún no se ha probado
Aquí hay una prueba de muestra:
Puedes ver más pruebas aquí:
https://github.com/VirtualTime/VirtualTime/blob/master/VirtualTimeLib.Tests/when_virtual_time_is_used.cs
Lo que le brinda la extensión DateTime.Now.ToVirtualTime es una instancia de ITime que pasa a un método / clase que depende de ITime. algunos DateTime.Now.ToVirtualTime se configuran en el contenedor DI de su elección
Aquí hay otro ejemplo inyectando en una clase contrustor
fuente
Estábamos usando un objeto SystemTime estático, pero tuvimos problemas para ejecutar pruebas unitarias paralelas. Intenté usar la solución de Henk van Boeijen pero tuve problemas en los hilos asincrónicos generados, terminé usando AsyncLocal de una manera similar a la siguiente:
Procedente de https://gist.github.com/CraftyFella/42f459f7687b0b8b268fc311e6b4af08
fuente
Una vieja pregunta, pero aún válida.
Mi enfoque es crear una nueva interfaz y clase para finalizar la
System.DateTime.Now
llamadaEsta interfaz se puede inyectar en cualquier clase que necesite obtener la fecha y hora actuales. En este ejemplo, tengo una clase que agrega un intervalo de tiempo a la fecha y hora actuales (una unidad comprobable System.DateTime.Now.Add (TimeSpan))
Y se pueden escribir pruebas para garantizar que funcione correctamente. Estoy usando NUnit y Moq pero cualquier marco de prueba servirá
Este patrón funcionará para cualquiera de las funciones que dependen de factores externos, como
System.Random.Next()
,System.DateTime.Now.UtcNow
,System.Guid.NewGuid()
etc.Consulte https://loadlimited.visualstudio.com/Stamina/_git/Stamina.Core para obtener más ejemplos u obtenga el paquete https://www.nuget.org/packages/Stamina.Core nuget.
fuente
Puede cambiar la clase que está probando para usar una
Func<DateTime>
que se pasará a través de sus parámetros de constructor, por lo que cuando crea una instancia de la clase en código real, puede pasar() => DateTime.UtcNow
aFunc<DateTime>
parámetro y, en la prueba, puede pasar el tiempo que Deseo probar.Por ejemplo:
fuente
Tengo el mismo problema, pero estaba pensando que no deberíamos usar las cosas de fecha y hora establecidas en la misma clase. porque podría conducir al mal uso algún día. así que he usado el proveedor como
Para las pruebas, en el proyecto de prueba se hizo un ayudante que se ocupará de las cosas establecidas,
en código
y en pruebas
Pero necesito recordar una cosa, en algún momento el DateTime real y el DateTime del proveedor no actúan igual
Supuse que la deferencia sería TimeSpan.FromMilliseconds máximo (0.00002) . Pero la mayoría de las veces es aún menos
Encuentre la muestra en MockSamples
fuente
Aquí está mi respuesta de esta pregunta. Combino el patrón 'Contexto ambiental' con IDisposable. Por lo tanto, puede usar el DateTimeProvider.Current en su código de programa normal y en la prueba anula el alcance con una instrucción de uso.
Aquí se explica cómo usar el DateTimeProvider anterior dentro de una prueba unitaria
fuente
Con el uso,
ITimeProvider
nos vimos obligados a llevarlo a un proyecto común compartido especial que debe ser referenciado desde el resto de otros proyectos. Pero esto complicó el control de las dependencias .Buscamos el
ITimeProvider
en .NET Framework. Buscamos el paquete NuGet y encontramos uno que no funcionaDateTimeOffset
.Así que se nos ocurrió nuestra propia solución, que depende solo de los tipos de la biblioteca estándar. Estamos usando una instancia de
Func<DateTimeOffset>
.Cómo utilizar
Cómo registrarse
Autofac
( Para futuros editores: adjunte sus casos aquí ).
Como prueba unitaria
fuente
Quizás una solución menos profesional pero más simple podría ser hacer un parámetro DateTime en el método del consumidor. Por ejemplo, en lugar de hacer un método como SampleMethod, haga SampleMethod1 con el parámetro. La prueba de SampleMethod1 es más fácil
fuente