Es estático universalmente "malo" para las pruebas unitarias y si es así, ¿por qué lo recomienda Resharper? [cerrado]

82

Descubrí que solo hay 3 formas de probar por unidad (simulacro / stub) las dependencias que son estáticas en C # .NET:

Dado que dos de estos no son gratuitos y uno no ha llegado a la versión 1.0, no es tan fácil burlarse de las cosas estáticas.

¿Eso hace que los métodos estáticos y tales "malos" (en el sentido de prueba de unidad)? Y si es así, ¿por qué Resharper quiere que haga algo que pueda ser estático, estático? (Suponiendo que resharper no es también "malo".)

Aclaración: Me refiero al escenario cuando desea probar un método de unidad y ese método llama a un método estático en una unidad / clase diferente . Según la mayoría de las definiciones de pruebas de unidad, si deja que el método en prueba llame al método estático en la otra unidad / clase, entonces no está haciendo pruebas de unidad de no , está haciendo pruebas de integración . (Útil, pero no una prueba de unidad.)

    
pregunta Vaccano 21.09.2010 - 03:18
fuente

11 respuestas

102

Mirando las otras respuestas aquí, creo que podría haber cierta confusión entre los métodos estáticos que mantienen el estado estático o causan efectos secundarios (que me suenan como una idea realmente mala) y los métodos estáticos que simplemente devuelven un valor.

Los métodos estáticos que no mantienen ningún estado ni causan efectos secundarios deben ser fácilmente verificables por la unidad. De hecho, considero que tales métodos son una forma de programación funcional del "hombre pobre"; le das al método un objeto o valor, y éste devuelve un objeto o valor. Nada mas. No veo cómo tales métodos afectarían negativamente a las pruebas unitarias en absoluto.

    
respondido por el Robert Harvey 21.09.2010 - 17:00
fuente
24

Parece que estás confundiendo los datos estáticos y los métodos estáticos. Resharper, si recuerdo correctamente, recomienda hacer métodos private dentro de una clase estática si se pueden hacer así. Creo que esto produce un pequeño beneficio de rendimiento. No no recomienda hacer "cualquier cosa que pueda ser" estática!

No hay nada malo con los métodos estáticos y son fáciles de probar (siempre y cuando no cambien ningún dato estático). Por ejemplo, piense en una biblioteca de Matemáticas, que es un buen candidato para una clase estática con métodos estáticos. Si tiene un método (ideado) como este:

public static long Square(int x)
{
    return x * x;
}

entonces esto es eminentemente comprobable y no tiene efectos secundarios. Solo verifica que cuando pase, digamos, 20, recupere 400. No hay problema.

    
respondido por el Dan Diplo 21.09.2010 - 17:29
fuente
17

Si la verdadera pregunta aquí es "¿Cómo pruebo este código?":

public class MyClass
{
   public void MethodToTest()
   {
       //... do something
       MyStaticClass.StaticMethod();
       //...more
   }
}

Luego, simplemente refactoriza el código e inyecta como de costumbre la llamada a la clase estática como esta:

public class MyClass
{
   private readonly IExecutor _externalExecutor;
   public MyClass(_IExecutor executor)
   {
       _exeternalExecutor = executor;
   }

   public void MethodToTest()
   {
       //... do something
       _exetrnalExecutor.DoWork();
       //...more
   }
}

public class MyStaticClassExecutor : IExecutor
{
    public void DoWork()
    {
        MyStaticClass.StaticMethod();
    }
}
    
respondido por el Sunny 15.08.2011 - 15:26
fuente
14

Echa un vistazo a esto: "Los métodos estáticos son muerte a prueba" . Breve resumen del argumento:

Para realizar una prueba unitaria, debe tomar una pequeña parte de su código, volver a cablear sus dependencias y probarlo de forma aislada. Esto es difícil con los métodos estáticos, no solo en el caso de que accedan al estado global, sino incluso si solo llaman a otros métodos estáticos.

    
respondido por el Rafał Dowgird 21.09.2010 - 09:37
fuente
13

Las estadísticas no son necesariamente malas, pero pueden limitar tus opciones cuando se trata de pruebas unitarias con falsificaciones / simulacros / talones.

Hay dos enfoques generales para burlarse.

El primero (tradicional - implementado por RhinoMocks, Moq, NMock2; también en este campo hay simulacros y muñones manuales) se basa en las costuras de prueba y la inyección de dependencia. Supongamos que está probando en unidad algún código estático y tiene dependencias. Lo que sucede a menudo en el código diseñado de esta manera es que las estadísticas crean sus propias dependencias, invirtiendo inversión de dependencia . Pronto descubres que no puedes inyectar interfaces simuladas en el código bajo prueba que está diseñado de esta manera.

El segundo (simulacro de todo, implementado por TypeMock, JustMock y Moles) se basa en . Puede interceptar cualquiera de sus instrucciones CIL y reemplazar una parte de su código con una falsificación. Esto permite que TypeMock y otros productos en este campo se burlen de cualquier cosa: estática, clases selladas, métodos privados, cosas que no están diseñadas para ser verificables.

Hay un debate en curso entre dos escuelas de pensamiento. Uno dice, siga los principios SÓLIDOS y diseñe para la capacidad de prueba (que a menudo incluye ser fácil con las estadísticas). El otro dice: compra TypeMock y no te preocupes.

    
fuente
5

La verdad simple que rara vez se reconoce es que si una clase contiene una dependencia visible del compilador en otra clase, no puede probarse de forma aislada de esa clase. Puede simular algo que parece una prueba y aparecerá en un informe como si fuera una prueba.

Pero no tendrá las propiedades de definición clave de una prueba; Falla cuando las cosas están mal, pasando cuando están bien.

Esto se aplica a cualquier llamada estática, llamada de constructor y cualquier referencia a métodos o campos no heredados de una clase base o interfaz . Si el nombre de la clase aparece en el código, es una dependencia visible para el compilador y no puede realizar una prueba válida sin él. Cualquier parte más pequeña simplemente no es una unidad comprobable válida . Cualquier intento de tratarlo como si fuera, tendrá resultados no más significativos que escribir una pequeña utilidad para emitir el XML utilizado por su marco de prueba para decir 'prueba aprobada'.

Dado eso, hay tres opciones:

  1. define la prueba de unidad para probar la unidad compuesta de una clase y sus dependencias codificadas. Esto funciona, siempre que evites las dependencias circulares.

  2. nunca cree dependencias en tiempo de compilación entre clases que sea responsable de probar. Esto funciona, siempre que no le importe el estilo del código resultante.

  3. no prueba de unidad, prueba de integración en su lugar. Lo que funciona, siempre que no entre en conflicto con otra cosa, necesita utilizar el término pruebas de integración.

respondido por el soru 01.03.2015 - 21:07
fuente
4

No hay dos maneras de hacerlo. Las sugerencias de ReSharper y varias características útiles de C # no se usarían tan a menudo si estuviera escribiendo pruebas de unidades atómicas aisladas para todo su código.

Por ejemplo, si tiene un método estático y necesita apagarlo, no puede hacerlo a menos que use un marco de aislamiento basado en perfiles. Una solución alternativa a la llamada es cambiar la parte superior del método para usar la notación lambda. Por ejemplo:

ANTES DE:

    public static DBConnection ConnectToDB( string dbName, string connectionInfo ) {
    }

DESPUÉS:

    public static Func<string, string, DBConnection> ConnectToDB (dbName, connectionInfo ) {
    };

Los dos son compatibles con la llamada. Las personas que llaman no tienen que cambiar. El cuerpo de la función sigue siendo el mismo.

Luego, en su código de prueba de unidad, puede apilar esta llamada así (suponiendo que esté en una clase llamada Base de datos):

        Database.ConnectToDB = (dbName, connectionInfo) => { return null|whatever; }

Tenga cuidado de reemplazarlo con el valor original una vez que haya terminado. Puede hacerlo a través de una prueba / finalmente o, en la limpieza de su prueba de unidad, la que recibe una llamada después de cada prueba, escriba un código como este:

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

que volverá a invocar el inicializador estático de su clase.

Lambda Funcs no es tan rico en soporte como los métodos estáticos regulares, por lo que este enfoque tiene los siguientes efectos secundarios no deseados:

  1. Si el método estático era un método de extensión, primero debe cambiarlo a un método sin extensión. Resharper puede hacer esto por ti automáticamente.
  2. Si alguno de los tipos de datos de los métodos estáticos es un conjunto de interoperabilidad incrustado, como en el caso de Office, debe ajustar el método, ajustar el tipo o cambiarlo al tipo 'objeto'.
  3. Ya no puedes usar la herramienta de refactorización de cambio de firma de Resharper.

Pero digamos que evitas las estáticas y conviertes esto en un método de instancia. Todavía no se puede simular a menos que el método sea virtual o esté implementado como parte de una interfaz.

Entonces, en realidad, cualquiera que sugiera remediar los métodos estáticos es convertirlos en métodos de instancia, también estarían en contra de los métodos de instancia que no son virtuales o forman parte de una interfaz.

Entonces, ¿por qué C # tiene métodos estáticos? ¿Por qué permite métodos de instancia no virtual?

Si usa cualquiera de estas "Características", simplemente no puede crear métodos aislados.

Entonces, ¿cuándo los usas?

Úsalos para cualquier código que no esperes que nadie quiera apagar. Algunos ejemplos:     El método Format () de la clase String.     El método WriteLine () de la clase Console.     el método Cosh () de la clase de Matemáticas

Y una cosa más ... La mayoría de las personas no se preocuparán por esto, pero si puedes sobre el rendimiento de una llamada indirecta, esa es otra razón para evitar los métodos de instancia. Hay casos en los que es un éxito de rendimiento. Es por eso que existen métodos no virtuales en primer lugar.

    
respondido por el zumalifeguard 01.03.2015 - 15:42
fuente
3
  1. Creo que es en parte porque los métodos estáticos son "más rápidos" de llamar que los métodos de instancia. (Entre comillas porque esto huele a micro optimización), vea enlace
  2. Le está diciendo que no necesita un estado, por lo tanto, se puede llamar desde cualquier lugar, eliminando la sobrecarga de instalación si eso es lo único que alguien necesita.
  3. Si quiero burlarme de él, entonces creo que es generalmente la práctica que se declara en una interfaz.
  4. Si se declara en una interfaz, R # no sugerirá que lo haga estático.
  5. Si se declara virtual, R # tampoco sugerirá que lo haga estático.
  6. El estado de retención (campos) estáticamente es algo que siempre debe considerarse con cuidado . El estado estático y los hilos se mezclan como el litio y el agua.

R # no es la única herramienta que hará esta sugerencia. FxCop / MS Code Analysis también hará lo mismo.

Por lo general, diría que si el método es estático, por lo general, también debería ser comprobable. Eso trae cierta consideración de diseño y probablemente más discusión de la que tengo en mis manos en este momento, por lo que espero pacientemente los votos negativos y los comentarios ...;)

    
respondido por el MIA 21.09.2010 - 05:38
fuente
3

Veo que después de mucho tiempo, nadie ha declarado un hecho realmente simple. Si Resharper me dice que puedo hacer un método estático, significa algo enorme para mí, puedo escuchar su voz que me dice: "Oye, estas piezas de lógica no son la RESPONSABILIDAD de la clase actual para manejar, por lo que debería mantenerse al margen. en alguna clase de ayuda o algo ".

    
respondido por el g1ga 29.11.2012 - 18:08
fuente
2

Si se llama al método estático desde dentro de otro método , no es posible evitar o sustituir dicha llamada. Esto significa, estos dos métodos hacen una sola unidad; Prueba de unidad de cualquier tipo prueba ambos.

Y si este método estático habla con Internet, conecta bases de datos, muestra ventanas emergentes de GUI o convierte la prueba de la Unidad en un desastre completo, simplemente lo hace sin ninguna solución fácil. Un método que llama a este método estático no se puede probar sin refactorizar, incluso si tiene muchos códigos puramente computacionales que se beneficiarían enormemente de ser sometidos a prueba de unidad.

    
respondido por el h22 18.10.2015 - 09:20
fuente
0

Creo que Resharper le da una guía y aplica las pautas de codificación con las que se ha configurado. Cuando he usado Resharper y me ha dicho que un método debería ser estático, está obligado a usar un método privado que no actúa en ninguna variable de instancia.

Ahora, en cuanto a testablidad, este escenario no debería ser un problema, ya que no debería probar métodos privados de todos modos.

En cuanto a la capacidad de prueba de los métodos estáticos que son públicos, las pruebas unitarias se vuelven difíciles cuando los métodos estáticos entran en estado estático. Personalmente, mantendría esto al mínimo y utilizaría los métodos estáticos como funciones puras tanto como sea posible donde se transfieran dependencias al método que se puede controlar a través de un dispositivo de prueba. Sin embargo, esta es una decisión de diseño.

    
respondido por el aqwert 21.09.2010 - 05:38
fuente

Lea otras preguntas en las etiquetas