2009-12-23 15 views
10

¿Cómo puedo escribir una prueba unitaria para un método que tiene una instrucción using?¿Cómo se prueba un método con una instrucción `using`?

Supongamos, por ejemplo, que tengo un método Foo.

public bool Foo() 
{ 
    using (IMyDisposableClass client = new MyDisposableClass()) 
    { 
     return client.SomeOtherMethod(); 
    } 
} 

¿Cómo puedo probar algo como el código de arriba?

A veces elijo no usar la declaración using y Dispose() un objeto manualmente. Espero que alguien me muestre un truco que pueda usar.

+0

¿Qué es TDD?La única expansión que conozco es Desarrollo basado en pruebas. –

+0

Es un desarrollo impulsado por prueba. – Vadim

+0

Entonces, su pregunta es "¿Cómo puedo probar el desarrollo de desarrollo algo así como el código anterior?" –

Respuesta

15

Si construye el IMyDisposableClass usando una fábrica (inyectado en la clase principal) en lugar de usar la palabra clave nueva, puede simular el IMyDisposable y hacer una verificación en la llamada al método de eliminación.

+0

Alternativamente, puede utilizar un método de fábrica virtual y probar una especialización de ese objeto que devuelva una instancia apropiada de simulacro. Al mirar el código, recuerde que el nuevo operador es una dependencia estática; existen otras buenas razones para evitar su uso también en objetos que no son de fábrica. – kyoryu

+0

¿Alguien puede dar un ejemplo de lo que significa esta respuesta? –

+0

@StephenPrice: En fábrica de clase constructor de inyección (IMyDisposableFactory MyDisposableFactory) y luego en Foo public bool() { usando (cliente IMyDisposableClass = MyDisposableFactory.CreateCl()) { retorno client.SomeOtherMethod(); } } –

2

Los métodos de envoltura como esos no son verificables por unidad, porque no puede especificar las condiciones previas o las condiciones posteriores relevantes.

Para hacer que el método comprobable, que tendrá que pasar una instancia IMyDisposableClass en el método o en la clase de alojamiento Foo (y hacer que la propia clase anfitrión implementar IDisposable), por lo que puede utilizar una prueba doble en vez de lo real cosa para verificar cualquier interacción con él.

-1

Si está probando Foo, entonces debería estar mirando la salida de Foo, sin preocuparse por la eliminación de la clase que está utilizando internamente.

Si desea probar MyDisposableClass 'eliminar el método para ver si está funcionando, debe ser una prueba de unidad separada construida contra MyDisposableClass.

No necesita probar el bloque using { }, ya que es parte del idioma. Confías en que está funcionando o no usas C#. :) No veo la necesidad de escribir una prueba unitaria para verificar que se llame al Dispose().

+0

No quiero probar MyDisposableClass Quiero simularlo. Jeff Sternal y mcintyre321 abajo tienen buenas sugerencias para que intente. – Vadim

+0

Nadie quiere probar el uso de {} bloque. El uso del {} bloque presenta una dependencia que no se puede inyectar ya que la creación de instancias de un objeto usando el bloque {} de uso se debe hacer en línea. Y dado que esa dependencia suele ser una capa de persistencia o algún código que no se gestiona en algún momento, es importante DI con un simulacro. Entonces la pregunta es, ¿cómo se DI un bloque Using {} para burlarse de la dependencia. – Suamere

16

Si ya tiene su código y está preguntando cómo probarlo, entonces no está escribiendo sus pruebas primero ... por lo que realmente no está haciendo TDD.

Sin embargo, lo que tiene aquí es una dependencia. Entonces el enfoque TDD sería usar Dependency Injection. Esto puede hacerse más fácil usando un contenedor IoC como Unity.

Al hacer TDD "correctamente", sus procesos de pensamiento deben funcionar de la siguiente manera en este tipo de escenario:

  • Necesito hacer un Foo
  • Para esto voy a confiar en una dependencia externa que se implementar una interfaz (nuevo o pre-existentes) de IMyDisposableClass
  • por lo tanto voy a inyectar un IMyDisposableClass en la clase en la que Foo se declara a través de su constructor

Luego escribiría una (o más) pruebas que fallan, y solo entonces estaría en el punto donde estaba escribiendo el cuerpo de la función Foo, y determinaría si necesitaba usar un bloque using.

En realidad, es posible que así saber que sí, que va a utilizar un bloque using. Pero parte del objetivo de TDD es que no debe preocuparse hasta que haya demostrado (mediante pruebas) que necesita usar un objeto que lo requiera.

Una vez que haya determinado que es necesario utilizar un bloque using usted entonces que desee escribir una prueba que falla - por ejemplo, usando algo como Rhino Mocks para establecer una expectativa de que Dispose se llamará en un objeto de burla que implementa IMyDisposableClass .

Por ejemplo (usando Rhino Mocks burlarse IMyDisposableClass).

[TestFixture] 
public class When_calling_Foo 
{ 
    [Test] 
    public void Should_call_Dispose() 
    { 
     IMyDisposableClass disposable = MockRepository 
             .GenerateMock<IMyDisposableClass>(); 

     Stuff stuff = new Stuff(disposable); 

     stuff.Foo(); 

     disposable.AssertWasCalled(x => x.Dispose()); 
    } 
} 

clase en la que existe la función Foo, con IMyDisposableClass inyectada como una dependencia:

public class Stuff 
{ 
    private readonly IMyDisposableClass _client; 

    public Stuff(IMyDisposableClass client) 
    { 
     _client = client; 
    } 

    public bool Foo() 
    { 
     using (_client) 
     { 
      return _client.SomeOtherMethod(); 
     } 
    } 
} 

Y la interfaz IMyDisposableClass

public interface IMyDisposableClass : IDisposable 
{ 
    bool SomeOtherMethod(); 
} 
+0

Lo siento Richard, pero su respuesta no es muy útil. Escribí un montón de código DI y utilicé muchos contenedores diferentes de Inversión de control, incluido Unity. Estoy trabajando en un código heredado que solo tiene pruebas de integración. Me gusta la respuesta mcintyre321 donde sugiere usar una fábrica. Es muy útil que puedo implementarlo. Si leyera mi pregunta por completo, leería "Algunas veces elijo no usar el uso de la declaración y Eliminar un objeto manualmente". – Vadim

+0

He modificado la pregunta para aclarar que esto está probando el código existente, no el TDD. –

+0

TBH es casi la misma respuesta que di, excepto que tendría el constructor de cosas como Stuff (Func myDisposableFactory) – mcintyre321

6

Su pregunta no tiene sentido. Si está usando TDD, entonces ya debería tener una prueba para lo que ha escrito. Requisitos, luego pruebas, luego diseño, luego desarrollo. O su código pasa sus pruebas, o no lo hace.

Ahora, si su pregunta es cómo unidad de prueba de la pieza de código anterior, entonces eso es otra cuestión completamente, y creo que los otros críticos han respondido hasta allí.

A veces pienso que hay más palabras de moda que los desarrolladores :)

+2

Amen. Es increíble ver a las personas luchar constantemente con la herramienta incorrecta (regex, LINQ) o el enfoque incorrecto (DDD) porque está "caliente" y solo quieren ser "geniales". Mas uno. – jason

0

Su pregunta no tiene sentido. Si está haciendo TDD, entonces el método que publicó es ya probado, de lo contrario, ni siquiera podría existir en primer lugar. Entonces, tu pregunta no tiene sentido.

Si, por otro lado, el método que publicó ya existe, pero no está completamente probado, entonces no está haciendo TDD de todos modos, y su pregunta sobre TDD tampoco tiene sentido.

En TDD, es simplemente imposible para que exista el código no probado. Período.

-1

Sin una especificación para Foo, ¿cómo podemos decir cómo probarla?

  1. Obtenga la especificación de Foo.
  2. pruebas de escritura para asegurar que cumple con todas las especificaciones y requisitos (o una subset- razonable algunas funciones podrían requerir cantidades prácticamente infinitas de los datos de prueba).

Creo que tiene una segunda pregunta implícita, que es cómo probar el uso de su MyDisposableClass correctamente. Elimina el objeto cuando se libera al salir de una cláusula using. Este es un problema de prueba separado, y no debe combinarse con la prueba de Foo, ya que la especificación de Foo no debe hacer referencia a los detalles específicos de la implementación, como el uso de MyDisposabeClass.

creo que los otros críticos han respondido a esta pregunta, por lo que no voy a dar más detalles.

Cuestiones relacionadas