¿Cómo burlarse de los métodos estáticos en c # usando el marco MOQ?

He estado haciendo pruebas unitarias recientemente y me burlé con éxito de varios escenarios con el uso del marco MOQ y las pruebas MS mediante la creación de pruebas unitarias. Como sé, no podemos hacer pruebas de métodos privados, pero utilizando la reflexión, pero quiero saber cómo podemos probar y simular pruebas unitarias mediante el uso de MOQ framework.

Moq (y otros marcos de burla basados ​​en DynamicProxy ) no pueden simular nada que no sea un método virtual o abstracto.

Las clases / métodos sellados / estáticos solo pueden falsificarse con las herramientas basadas en Profiler API, como Typemock (comercial) o Microsoft Moles (gratis, conocido como Fakes en Visual Studio 2012 Ultimate / 2013/2015 ).

Alternativamente, podría refactorizar su diseño para llamadas abstractas a métodos estáticos y proporcionar esta abstracción a su clase a través de la dependency injection. Entonces no solo tendrías un mejor diseño, sino que también se podrá probar con herramientas gratuitas, como Moq.

Se puede aplicar un patrón común para permitir la capacidad de prueba sin utilizar ninguna herramienta por completo. Considera el siguiente método:

public class MyClass { public string[] GetMyData(string fileName) { string[] data = FileUtil.ReadDataFromFile(fileName); return data; } } 

En lugar de intentar FileUtil.ReadDataFromFile , podría envolverlo en un método protected virtual , como este:

 public class MyClass { public string[] GetMyData(string fileName) { string[] data = GetDataFromFile(fileName); return data; } protected virtual string[] GetDataFromFile(string fileName) { return FileUtil.ReadDataFromFile(fileName); } } 

Luego, en su prueba de unidad, derive de MyClass y TestableMyClass . Luego puede anular el método GetDataFromFile para devolver sus propios datos de prueba.

Espero que ayude.

Otra opción para transformar el método estático en Func o Action estático. Por ejemplo.

Código original:

  class Math { public static int Add(int x, int y) { return x + y; } 

Desea “burlarse” del método Add, pero no puede. Cambie el código anterior a esto:

  public static Func Add = (x, y) => { return x + y; }; 

El código de cliente existente no tiene que cambiar (tal vez recomstackr), pero la fuente sigue siendo la misma.

Ahora, desde la prueba unitaria, para cambiar el comportamiento del método, simplemente reasigne una función en línea a él:

  [TestMethod] public static void MyTest() { Math.Add = (x, y) => { return 11; }; 

Escriba la lógica que desee en el método, o simplemente devuelva un valor codificado, según lo que intente hacer.

Esto puede no ser necesariamente algo que puedas hacer cada vez, pero en la práctica, encontré que esta técnica funciona bien.

[edit] Sugiero que agregue el siguiente código de limpieza a su clase de prueba de unidad:

  [TestCleanup] public void Cleanup() { typeof(Math).TypeInitializer.Invoke(null, null); } 

Agregue una línea separada para cada clase estática. Lo que hace es que, una vez que la prueba de la unidad se ejecuta, restablece todos los campos estáticos a su valor original. De esta forma, otras pruebas unitarias en el mismo proyecto comenzarán con los valores predeterminados correctos en comparación con la versión simulada.

Moq no puede burlarse de un miembro estático de una clase.

Al diseñar el código para la capacidad de prueba, es importante evitar los miembros estáticos (y los únicos). Un patrón de diseño que puede ayudarlo a refactorizar su código para la capacidad de prueba es Inyección de Dependencia.

Esto significa cambiar esto:

 public class Foo { public Foo() { Bar = new Bar(); } } 

a

 public Foo(IBar bar) { Bar = bar; } 

Esto le permite usar un simulacro de las pruebas de su unidad. En producción, utiliza una herramienta de dependency injections como Ninject o Unity que puede conectar todo junto.

Escribí un blog sobre esto hace algún tiempo. Explica qué patrones se utilizarán para un mejor código comprobable. Tal vez te pueda ayudar: Pruebas unitarias, infierno o cielo?

Otra solución podría ser utilizar el Microsoft Fakes Framework . Esto no reemplaza la redacción de un código comprobable bien diseñado, pero puede ser útil. El marco Fakes le permite simular miembros estáticos y reemplazarlos en tiempo de ejecución con su propio comportamiento personalizado.

Como se menciona en las otras respuestas, MOQ no puede simular métodos estáticos y, como regla general, se debe evitar la estática siempre que sea posible.

Algunas veces no es posible. Uno está trabajando con código heredado o de terceros o incluso con los métodos BCL que son estáticos.

Una posible solución es envolver la estática en un proxy con una interfaz que puede ser burlada

  public interface IFileProxy { void Delete(string path); } public class FileProxy : IFileProxy { public void Delete(string path) { System.IO.File.Delete(path); } } public class MyClass { private IFileProxy _fileProxy; public MyClass(IFileProxy fileProxy) { _fileProxy = fileProxy; } public void DoSomethingAndDeleteFile(string path) { // Do Something with file // ... // Delete System.IO.File.Delete(path); } public void DoSomethingAndDeleteFileUsingProxy(string path) { // Do Something with file // ... // Delete _fileProxy.Delete(path); } } 

El inconveniente es que el controlador puede llegar a estar muy desordenado si hay muchos proxies (aunque podría argumentarse que si hay muchos proxies, entonces la clase puede estar tratando de hacer demasiado y podría ser refactorizada)

Otra posibilidad es tener un ‘proxy estático’ con diferentes implementaciones de la interfaz detrás de él

  public static class FileServices { static FileServices() { Reset(); } internal static IFileProxy FileProxy { private get; set; } public static void Reset(){ FileProxy = new FileProxy(); } public static void Delete(string path) { FileProxy.Delete(path); } } 

Nuestro método ahora se convierte

  public void DoSomethingAndDeleteFileUsingStaticProxy(string path) { // Do Something with file // ... // Delete FileServices.Delete(path); } 

Para las pruebas, podemos establecer la propiedad FileProxy en nuestro simulacro. El uso de este estilo reduce el número de interfaces que se inyectarán, pero hace que las dependencias sean menos obvias (aunque no más que las llamadas estáticas originales, supongo).