2009-09-25 23 views
7

Intento seguir buenas prácticas al escribir mis suites de prueba. A mitad de me di cuenta de que el gasto iam mucho (más) de mi tiempo en los falsos objetos ... La mayoría de mis pruebas hace algo como esto¿Pruebas unitarias con falsificaciones o burlas?

public interface ITemplateRepository 
{ 
    string get GetGenericTemplate {get;} 
} 


public FakeTemplateRepository : ITemplateRepository 
{ 
    public string GetGenericTemplate() 
    { 
    return "<xml>Complex</xml>"; 
    } 
} 


[Test] 
public void CanGetGenericTemplate() 
{ 
    ITemplateRepository rep = new FakeTemplateRepository(); 
    Assert.IsNotNull(rep.GetGenericTemplate()); 
} 


[Test] 
public void GetGenericTemplateContains() 
{ 
ITemplateRepository rep = new FakeTemplateRepository(); 
Assert.IsTrue(rep.GetGenericTemplate().StartsWith("<xml>")); 
} 

realidad me encontré pasar mucho tiempo en el FakeTemplateRepository, asegurándose de que regresó el contenido real que estaba esperando. ¿Esto es malo?

¿Sigue siendo una prueba de unidad válida? Las pruebas unitarias deben ser rápidas y simples, ¿no? Pero, sinceramente, no estoy muy seguro, una cosa es segura: me ayudó a pensar sobre la forma y el contenido de mis datos. El contenido en FakeRepository reflejará más o menos mi contenido de producción, aunque leyendo desde el sistema de archivos, en lugar de en la memoria.

Si, de hecho, lo que estoy haciendo son pruebas de integración, ¿cómo debería usar los simulacros para las pruebas de mi unidad?

No tiene sentido para mí (si utilizo Mocks) que configuro la expectativa para que llame al método y devuelva una cadena? ¿Me estoy perdiendo algo pero no veo mucho valor allí? ¡El código ni siquiera compilará si configuro un nombre de método inválido!

Realmente estoy muy confundido sobre el asunto y mi percepción de las pruebas unitarias es totalmente borrosa ahora con todos estos conceptos.

¿Alguien puede demostrar con un ejemplo súper súper simple de cómo las imitaciones y burlas caben en una suite? Por ejemplo, ¿qué debería ser una prueba unitaria? ¿Qué debería ser una prueba de integración?

Gracias

+0

Bueno, darle a tu código lo que quiere no va a ser muy útil ya que probar en su forma más simple es, supongo, "ponerlo a prueba". –

Respuesta

9

Una vez más ... otra persona cae en un simulacro anti-patrón. Estás "probando un simulacro" o un objeto falso, que no sirve para nada. Mocks tienen que ser uso de abstraer las dependencias o colaboradores de su sujeto de prueba .. no es la prueba en sí sujeto.

Primero, yo diría que leí el documento de Martin Fowler "Mocks Aren't Stubs". Si desea devolver valores o configurar expectativas para argumentos específicos, busque un marco de burla sobre las falsificaciones Roll-to-own. Las falsificaciones generalmente se usan para silenciar a los colaboradores ... en los que no se preocupa por el colaborador ... p. stub out network o file IO.
Siguiente Lee this similar SO question y las respuestas a ella ..

2

En primer lugar, aprender un marco de aislamiento (burla), al igual que Moq o Rhino Mocks. Esto te ahorrara mucho tiempo.

En segundo lugar, evitar la creación de burla siempre que sea posible y se adhieren a los talones. Los apéndices solo devuelven los valores; usted no afirma contra ellos. En cambio, afirmas que el estado de la clase bajo prueba es lo que esperabas que fuera. Use stubs para cerrar las dependencias inyectadas.

Este es un tema complejo, y tendrá que leer un poco.Osherove's Art of Unit Testing es barato en su forma de libro electrónico, y hace un buen trabajo al explicar esto. También querrá leer en Dependency Injection.

Recuerde que no necesita falsificaciones, burlas o talones para probar muchas clases. El punto es probar el comportamiento público de la clase real de forma aislada. Las falsificaciones son simplemente una herramienta para ayudarlo a aislar la clase de cualquier dependencia.

Sus ejemplos se concentran en falsificaciones, interfaces y pruebas. Concéntrese en la clase bajo prueba y su comportamiento deseado.

Las pruebas de integración también tienen valor; puede conectar todo a una base de datos de prueba y asegurarse de que todo funcione. Tienden a ser lentos, a diferencia de las pruebas unitarias.

EDIT: Ahora estoy leyendo Growing Object-Oriented Software, Guided by Tests y cuestionando mi punto de vista "evitar burlarse". Si tiene objetos que siguen Tell, Don't Ask, las pruebas de estado pueden ser difíciles.

5

En su ejemplo, está probando su falso. No puedes entender esto porque no tiene sentido.

Aquí hay un ejemplo de cómo debe usar un falso. Desea probar la clase DocumentGenerator por unidad. Esta clase pasa a usar los servicios de un ITemplateRepository. Sin embargo, para probar adecuadamente DocumentGenerator, debe asegurarse de que no dependa de ningún servicio externo. Por lo tanto, le proporciona a DocumentGenerator un ITemplateRepository falso que devolverá los resultados almacenados.

Aquí se muestra el código.

public class DocumentGenerator 
{ 
    ITemplateRepository _repo; 

    DocumentGenerator(ITemplateRepository repo) 
    { 
    _repo = repo; 
    } 

    Doc GenerateDoc() 
    { 
    .... 
    _repo.GetGenericTemplate(); 
    .... 
    } 
} 


[Test] 
public void GenerateDocTest() 
{ 
    ITemplateRepository rep = new FakeTemplateRepository(); 
    DocumentGenerator docGenerator = new DocumentGenerator(rep); 
    Assert.IsNotNull(docGenerator.GenerateDoc()); 
} 

La esencia del ejemplo es que las pruebas algo que utiliza una falsificación. No estás probando el falso en sí mismo. La falsificación existe para que lo que está probando no dependa de algo externo.

+0

¿Qué sucede si DocumentGenartor no acepta un repositorio sino un valor de cadena de algo que se devuelve desde el repositorio? es decir ,DocumentGenerator (plantilla de cadena). Si tuviera que hacer algo como esto, ¿sugiere que tengo el diseño equivocado? –

+0

Si DocumentGenartor solo toma un String, entonces no necesita el objeto falso en absoluto. Solo necesita crear objetos falsos para los colaboradores que realmente usa la unidad bajo prueba. –