2011-07-08 18 views
12

Actualmente mi proyecto se compone de varias clases concretas. Ahora que estoy entrando en las pruebas unitarias, parece que se supone que debo crear una interfaz para cada clase (¿se dobla efectivamente el número de clases en mi proyecto)? Resulta que estoy usando Google Mock como un marco de burla. Ver Google Mock CookBook on Interfaces. Mientras que antes podría tener solo las clases Car y Engine, ahora tendría clases abstractas (también conocidas como interfaces C++) Car y Engine y luego las clases de implementación CarImplementation y EngineImpl o lo que sea. Esto me permitiría anular la dependencia de Car en Engine.Pruebas unitarias: ¿codificación en las interfaces?

Hay dos líneas de pensamiento que he encontrado en la investigación de este:

  1. Sólo use las interfaces en las que puede tener la necesidad de más de un implementación de una abstracción dada y/o para su uso en public APIs, , de lo contrario, no cree interfaces innecesariamente.

  2. pruebas unitarias talones/burla menudo son la "otra aplicación", y por lo tanto, sí, se deben crear intefaces.

Cuando estoy probando la unidad, ¿debo crear una interfaz para cada clase en mi proyecto? (Me inclino por crear interfaces para facilitar la prueba)

Respuesta

0

Crear interfaces para cada clase dentro de su proyecto puede o no ser necesario. Esto es completamente una decisión de diseño. He descubierto que, en general, no lo es. A menudo, en el diseño n-tier, desea abstraer la capa entre el acceso a los datos y la lógica. Yo diría que debe trabajar en esto, ya que ayuda a probar la lógica sin mucha infraestructura necesaria para las pruebas. Los métodos de abstracción como dependency injection and IoC requerirían que hagas algo como esto y harían más fácil probar dicha lógica.

Examinaré lo que está tratando de probar y me concentraré en las áreas que considera más propensas a error. Esto puede ayudarlo a decidir si las interfaces son necesarias.

1

hay dos categorías de las pruebas relativas a la visibilidad de aplicación: las pruebas de recuadro negro y caja blanca pruebas

  • las pruebas de recuadro negro se centra en la aplicación de pruebas a través de sus interfaces, y validar el ajuste a sus especificaciones.

  • pruebas de caja blanca pruebas granulares detalles acerca de la implementación que NO, en general, debe ser accesible desde el exterior. Este tipo de prueba validará que los componentes de implementación funcionan según lo previsto. Por lo que sus resultados son en su mayoría de interés para los desarrolladores tratando de averiguar lo que está roto, o necesita mantainance

burla por su ajuste en la definición de arquitecturas modulares, pero no se deduce que todas las clases en un proyecto necesitan ser completamente modular por sí mismos. Está perfectamente bien trazar una línea cuando un grupo de clases se conocerán entre sí. Ellos como grupo pueden presentar a otros módulos desde la perspectiva de alguna clase de interfaz de fachada. Sin embargo, aún querrá tener controladores de prueba de caja blanca dentro de este módulo con conocimiento sobre los detalles de implementación.Por lo tanto, este tipo de prueba es que no es una buena opción para los simulacros.

De esto se deduce trivialmente que no necesita tener burlas o interfaces para todo. Simplemente tome los componentes de diseño de alto nivel que implementan interfaces de fachada y cree burlas para ellos. Se le dará el punto dulce, donde las pruebas de simulacro vale la pena mi humilde opinión

una vez dicho esto, trate de usar la herramienta a sus necesidades, en lugar de dejar la fuerza herramienta en cambios cree que no será beneficioso en el largo ejecutar

+0

Gracias por su respuesta; Estoy intentando asimilarlo. ¿Estás diciendo que las pruebas de Whitebox no son una buena opción para los simulacros? La forma en que estoy viendo las pruebas unitarias es que estoy tratando de probar cada unidad de código. Entonces, por ejemplo, quiero probar 'Car' y' Car' depende de una clase 'Engine'. Para asegurarme de que estoy haciendo una prueba de * unit * y no de * integración *, * necesito * burlarme de la clase 'Engine' (y de una forma u otra obtengo' Car' para usar mi 'Engine' burlado) . De lo contrario, estoy probando dos clases y haciendo una prueba de integración, no una prueba de unidad. Y para burlarse fácilmente de la clase 'Engine' parece que necesita ser una interfaz. – User

+0

la única diferencia aquí es qué representa * una * unidad. Por lo general, para algunas personas una unidad que se probará representa una unidad de compilación, o una clase, pero no tiene que ser así. Por ejemplo, si crea una clase de gráfico, nodo y borde estarán altamente acoplados entre sí, por lo que es bastante inútil probarlos de forma aislada.Simplemente envuelva todo el módulo gráfico en una interfaz de fachada y cuando otros módulos necesiten interactuar con una representación simulada de gráfico, simule la fachada del gráfico, no las clases individuales (como una fachada adecuada, necesitará representaciones de nivel de API de los nodos, como enteros o id) – lurscher

+0

En mi comprensión de las pruebas unitarias, si la clase edge usa la clase de nodo, entonces cuando estoy probando la clase de borde, me gustaría burlarme de la clase de nodo porque no quiero probar dos "unidades" al mismo tiempo. Si falla una prueba de clase de borde, no sabría si fue el borde o el nodo el que realmente falló. Creo que llamaría a lo que estás diciendo sobre las pruebas de integración (darte cuenta de que es solo semántica hasta cierto punto). – User

5

Creo que tienes varias opciones. Como dices, una opción es crear interfaces. Digamos que tienes clases

class Engine: 
{ 
public: 
    void start(){ }; 
}; 

class Car 
{ 
public: 
    void start() 
    { 
     // do car specific stuff 
     e_.start(); 

private: 
    Engine e; 
}; 

introducir interfaces - que tendría que cambiar de coche para tomar algún motor

class Car 
{ 
public: 
    Car(Engine* engine) : 
    e_(engine) 
    {} 

    void start() 
    { 
     // do car specific stuff 
     e_->start(); 

private: 
    Engine *e_; 
}; 

Si sólo tiene un tipo de motor - que ha hecho de repente su coche objetos más difíciles de usar (quién crea los motores, a quién pertenecen los motores). Los automóviles tienen muchas partes, por lo que este problema seguirá aumentando.

Si desea implementaciones separadas, otra forma sería con plantillas. Esto elimina la necesidad de interfaces.

class Car<type EngineType = Engine> 
{ 
public: 
    void start() 
    { 
     // do car specific stuff 
     e_.start(); 

private: 
    EngineType e; 
}; 

En su burla, entonces podría crear vehículos con motores especializados:

Car<MockEngine> testEngine; 

Otro enfoque diferente, sería añadir métodos a motor para que pueda ser probado, algo así como:

class Engine: 
{ 
public: 
    void start(); 
    bool hasStarted() const; 
}; 

Puede añadir un método de verificación al automóvil o heredarlo del automóvil para realizar la prueba.

class TestCar : public Car 
{ 
public: 
    bool hasEngineStarted() { return e_.hasStarted(); } 
}; 

Esto requeriría que el motor se cambie de privado a protegido en la clase de automóvil.

Dependiendo de la situación del mundo real, dependerá de qué solución es la mejor. Además, cada desarrollador tendrá su propio santo grial de cómo creen que el código debe ser probado en unidades. Mi punto de vista personal es mantener al cliente/cliente en mente. Asumamos que sus clientes (quizás otros desarrolladores en su equipo) crearán automóviles y no se preocupan por los motores. Por lo tanto, no quisiera exponer los conceptos de Motores (una clase interna a mi biblioteca) solo para que pueda probar la unidad. Optaría por no crear interfaces y probar las dos clases juntas (tercera opción que di).

Cuestiones relacionadas