2011-05-03 26 views
51

Recientemente, he empezado a usar Moq para probar la unidad. Uso Moq para simular clases que no necesito probar.Mocking Static Methods

¿Cómo manejas típicamente los métodos estáticos?

public void foo(string filePath) 
{ 
    File f = StaticClass.GetFile(filePath); 
} 

¿Cómo podría este método estático, StaticClass.GetFile() conseguir burlado?

P.S. Agradecería cualquier material de lectura que recomiende en Moq y Unit Testing.

Respuesta

38

Los frameworks burladores como Moq o Rhinomocks solo pueden crear instancias simuladas de objetos, esto significa que no es posible burlar los métodos estáticos.

También puede search Google para obtener más información.

Además, hay algunas preguntas formuladas anteriormente en StackOverflow here, here y here.

32

@ Pure.Krome: buena respuesta, pero voy a añadir algunos detalles

@ Kevin: Tienes que elegir una solución en función de los cambios que se pueden llevar al código.
Si puede cambiarlo, algunas inyecciones de dependencia hacen que el código sea más comprobable. Si no puede, necesita un buen aislamiento.
Con el marco de simulación gratuito (Moq, RhinoMocks, NMock ...), solo puede simular delegados, interfaces y métodos virtuales. Por lo tanto, para los métodos estáticos, sellada y no virtuales que tiene 3 soluciones:

  • Typemock aislador (puede burlarse de todo, pero es caro)
  • JustMock de Telerik (recién llegado, menos costosos, pero aún no libre)
  • Moles de Microsoft (la única solución libre para aislamiento)

Recomiendo Moles, porque es gratis, eficiente y usa expresiones lambda como Moq. Solo un detalle importante: los moles proporcionan talones, no burlas. Por lo que puede seguir utilizando Moq para la interfaz y los delegados;)

Mock: una clase que implementa una interfaz y permite la posibilidad de configurar dinámicamente los valores de retorno/excepciones a tiro de métodos particulares y proporciona la capacidad para verificar si se han llamado/no llamado métodos particulares.
Codigo auxiliar: Como una clase falsa, excepto que no proporciona la capacidad de verificar que los metodos han sido llamados/no llamados.

+7

como una actualización de este. Moles ahora se llama Fakes y está integrado en Visual Studio 2012: http://msdn.microsoft.com/en-us/library/hh549175.aspx – gscragg

+2

En Moles puede verificar si se ha llamado a un método simplemente agregando una variable booleana y al establecerlo dentro de la función stub también se pueden verificar todos los parámetros en la llamada con este workarround. – mart

+0

Hola Jeco ... Tengo una idea de tu respuesta anterior. ¿Podría responder una pregunta en http://stackoverflow.com/questions/27621085/moles-shims-frameworks-other-than-introduced-by-microsoft –

13

Hay posibilidad en .NET excluyendo MOQ y cualquier otra biblioteca burlona. Debe hacer clic con el botón derecho en el explorador de soluciones en el ensamblaje que contiene el método estático que desea simular y elegir Añadir conjunto de falsificaciones.A continuación, puede burlarse libremente de los métodos estáticos de ensamblaje.

Supongamos que desea simular System.DateTime.Now método estático. Haga esto, por ejemplo, de esta manera:

using (ShimsContext.Create()) 
{ 
    System.Fakes.ShimDateTime.NowGet =() => new DateTime(1837, 1, 1); 
    Assert.AreEqual(DateTime.Now.Year, 1837); 
} 

Tiene una propiedad similar para cada propiedad y método estático.

+6

Una advertencia para las personas que lo encuentran: MS Fakes está incorporado solo en Visual Studio 2012+ Premium/Ultimate. Probablemente no podrá integrarlo en una cadena de integración continua. – thomasb

+2

Esta debería ser la respuesta aceptada. MS Fakes con calzas ahora hace que sea muy posible simular métodos estáticos. – Jez

+0

¡TENGA MUCHO cuidado al usar MS Fakes! Fakes es un marco de redirección, y su uso conduce rápidamente a una arquitectura deficiente. Solo use Fakes cuando sea absolutamente necesario para interceptar llamadas a bibliotecas de terceros o (muy) antiguas. La mejor solución es crear un contenedor de clase con una interfaz, y luego pasar la interfaz a través de la inyección del constructor o mediante un marco de inyección. Burlarse de la interfaz, y luego pasar eso en el código bajo prueba. Manténgase alejado de las falsificaciones, si es posible. –

-1

Sé que esto es un poco tarde, pero esta solución indirecta me permitió simular un método estático con Moq.

Para hacer esto, creé una clase (llamémoslo Placeholder) cuyo método llamó al método estático StaticClass.GetFile.

public class Placeholder{ 

    //some empty constructor 

    public File GetFile(){ 

     File f = StaticClass.GetFile(filePath); 
     return f; 
    } 
} 

Entonces, en lugar de llamar StaticClass.GetFile en foo, he creado una instancia de Placeholder y llamó a la función GetFile.

public void foo(string filePath) 
{ 
    Placeholder p = new Placeholder(); 
    File f = p.GetFile(filePath); 
} 

Ahora, en las pruebas de unidad, en lugar de tratar de burlarse StaticClass.GetFile, yo era capaz de burlarse de la GetFile método no estático de la clase Placeholder.

+0

Falta una interfaz y cómo simular –

0

He estado jugando con un concepto de refactorización de los métodos estáticos para invocar a un delegado que puede establecer externamente para fines de prueba.

Esto no utilizaría ningún marco de prueba y sería una solución completamente a medida, sin embargo, el refactor no influirá en la firma de la persona que llama y por lo tanto sería relativamente seguro.

Para que esto funcione, debe tener acceso al método estático, por lo que no funcionaría para ninguna biblioteca externa como System.DateTime.

Heres un ejemplo en el que he estado jugando donde he creado un par de métodos estáticos, uno con un tipo de devolución que toma dos parámetros y un genérico que no tiene tipo de devolución.

La principal clase estática:

public static class LegacyStaticClass 
{ 
    // A static constructor sets up all the delegates so production keeps working as usual 
    static LegacyStaticClass() 
    { 
     ResetDelegates(); 
    } 

    public static void ResetDelegates() 
    { 
     // All the logic that used to be in the body of the static method goes into the delegates instead. 
     ThrowMeDelegate = input => throw input; 
     SumDelegate = (a, b) => a + b; 
    } 

    public static Action<Exception> ThrowMeDelegate; 
    public static Func<int, int, int> SumDelegate; 

    public static void ThrowMe<TException>() where TException : Exception, new() 
     => ThrowMeDelegate(new TException()); 

    public static int Sum(int a, int b) 
     => SumDelegate(a, b); 
} 

Las pruebas unitarias (xUnit y Shouldly)

public class Class1Tests : IDisposable 
{ 
    [Fact] 
    public void ThrowMe_NoMocking_Throws() 
    { 
     Should.Throw<Exception>(() => LegacyStaticClass.ThrowMe<Exception>()); 
    } 

    [Fact] 
    public void ThrowMe_EmptyMocking_DoesNotThrow() 
    { 
     LegacyStaticClass.ThrowMeDelegate = input => { }; 

     LegacyStaticClass.ThrowMe<Exception>(); 

     true.ShouldBeTrue(); 
    } 

    [Fact] 
    public void Sum_NoMocking_AddsValues() 
    { 
     LegacyStaticClass.Sum(5, 6).ShouldBe(11); 
    } 

    [Fact] 
    public void Sum_MockingReturnValue_ReturnsMockedValue() 
    { 
     LegacyStaticClass.SumDelegate = (a, b) => 6; 
     LegacyStaticClass.Sum(5, 6).ShouldBe(6); 
    } 

    public void Dispose() 
    { 
     LegacyStaticClass.ResetDelegates(); 
    } 
}