sencillos resueltos que programas poo polimorfismo objeto ejercicios ejemplos ejemplo comandos codigo clases c++ testing mocking

resueltos - falsos/simulacros de métodos no virtuales de C++



ejemplos sencillos de polimorfismo en c++ (8)

Se sabe que en C ++ la burla / falsificación de métodos no virtuales para la prueba es difícil. Por ejemplo, el libro de cocina de googlemock tiene dos sugerencias: ambas significan modificar el código fuente original de alguna manera (plantillas y reescritura como interfaz).

Parece que este es un problema muy malo para el código C ++. ¿Cómo se puede hacer mejor si no puede modificar el código original que se debe simular o simular? Duplicar todo el código / clase (¿con toda la jerarquía de clases base?)


@zaharpopov puede usar Typemock IsolatorPP para crear simulacros de clases y métodos no virtuales sin cambiar su código (o código heredado). por ejemplo, si tiene una clase no virtual llamada MyClass :

class MyClass { public: int GetResult() { return -1; } }

Puedes burlarte de ella con typemock así:

MyClass* fakeMyClass = FAKE<MyClass>(); WHEN_CALLED(fakeMyClass->GetResult()).Return(10);

Por cierto, las clases o los métodos que desea probar también pueden ser privados, ya que typemock también puede burlarse de ellos, por ejemplo:

class MyClass { private: int PrivateMethod() { return -1; } } MyClass* myClass = new MyClass(); PRIVATE_WHEN_CALLED(myClass, PrivateMethod).Return(1);

Para más información entra here .


Creo que no es posible hacerlo con C ++ estándar en este momento (pero esperemos que pronto llegue a C ++ una poderosa reflexión en tiempo de compilación ...). Sin embargo, hay una serie de opciones para hacerlo.

Podrías echar un vistazo a Injector++ . Es solo Windows ahora mismo, pero planea agregar soporte para Linux y Mac.

Otra opción es CppFreeMock , que parece funcionar con GCC, pero no tiene actividades recientes.

HippoMocks también proporciona dicha capacidad, pero solo para funciones gratuitas. No lo admite para las funciones de miembro de clase.

No estoy completamente seguro, pero parece que todo lo anterior logra esto al sobrescribir la función objetivo en tiempo de ejecución para que salte a la función falsa.

Allí está C-Mock , que es una extensión de Google Mock que le permite simular funciones no virtuales al redefinirlas y confiar en el hecho de que las funciones originales están en bibliotecas dinámicas. Está limitado a la plataforma GNU / Linux.

Finalmente, también PowerFake probar PowerFake (para el cual soy el autor) tal como se presenta here .

No es un marco burlón (actualmente) y brinda la posibilidad de reemplazar las funciones de producción con las de prueba. Espero poder integrarlo en uno o más marcos de burla; Si no, se convertirá en uno.

También anula la función original durante el enlace (por lo tanto, no funcionará si se llama a una función en la misma unidad de traducción en la que está definida), pero utiliza un truco diferente al de C-Mock, ya que usa GNU ld''s --wrap opción. También necesita algunos cambios en su sistema de compilación para las pruebas, pero no afecta el código principal de ninguna manera (excepto si se le obliga a poner una función en un archivo .cpp separado); pero se proporciona soporte para integrarlo fácilmente en proyectos CMake.

Pero, actualmente está limitado a GCC / GNU ld (funciona también con MinGW).


El código debe escribirse para ser comprobable, según las técnicas de prueba que utilice. Si desea realizar pruebas utilizando simulacros, eso significa algún tipo de inyección de dependencia.

Las llamadas no virtuales que no dependen de un parámetro de plantilla plantean el mismo problema que static métodos static y final en Java [*]: el código en prueba dijo explícitamente: "Quiero llamar a este código, no a un bit de código desconocido que sea dependiente de alguna manera en un argumento ". Usted, el probador, desea que llame a un código diferente a prueba de lo que normalmente llama. Si no puede cambiar el código bajo prueba, entonces usted, el evaluador, perderá ese argumento. También puede preguntar cómo introducir una versión de prueba de la línea 4 de una función de 10 líneas sin cambiar el código bajo prueba.

Si la clase a simular está en una TU diferente de la clase bajo prueba, puede escribir un simulacro con el mismo nombre que el original y vincularlo. Si no puede generar ese simulacro usando su marco de burla de la manera normal, no estoy tan seguro.

Si lo desea, supongo que es un "muy mal problema para C ++" que es posible escribir código que es difícil de probar. Comparte este "problema" con un gran número de otros idiomas ...

[*] Mi conocimiento de Java es bastante bajo. Puede haber alguna forma inteligente de burlarse de tales métodos en Java, que no son aplicables a C ++. Si es así, por favor ignórelos para ver la analogía ;-)


Eso es más fácil de lo que piensas. Simplemente pase el objeto construido al constructor de la clase que está probando. En la clase almacenar la referencia a ese objeto. Entonces es fácil usar clases simuladas.

EDITAR:

El objeto que le está pasando al constructor necesita una interfaz, y esa clase almacena solo la referencia a la interfaz.

struct Abase { virtual ~Abase(){} virtual void foo() = 0; }; struct Aimp : public Abase { virtual ~Aimp(){} virtual void foo(){/*do stuff*/} }; struct B { B( Aimp &objA ) : obja( objA ) { } void boo() { objA.foo(); } Aimp &obja; }; int main() { //... Aimp realObjA; B objB( realObjA ); // ... }

En la prueba, puedes pasar el objeto simulado fácilmente.


Seguí el enlace Link Seam de la respuesta de sdg . Allí leí sobre diferentes tipos de costuras, pero me impresionaron mucho las Costuras de preprocesamiento. Esto me hizo pensar en explotar aún más el preprocesador. Resultó que es posible simular una dependencia externa sin cambiar realmente el código de llamada .

Para hacer esto, debe compilar el archivo fuente de llamada con una definición de dependencia sustituta. Aquí hay un ejemplo de cómo hacerlo.

dependencia.h

#ifndef DEPENDENCY_H #define DEPENDENCY_H class Dependency { public: //... int foo(); //... }; #endif // DEPENDENCY_H

caller.cpp

#include "dependency.h" int bar(Dependency& dependency) { return dependency.foo() * 2; }

test.cpp

#include <assert.h> // block original definition #define DEPENDENCY_H // substitute definition class Dependency { public: int foo() { return 21; } }; // include code under test #include "caller.cpp" // the test void test_bar() { Dependency mockDependency; int r = bar(mockDependency); assert(r == 42); }

Tenga en cuenta que el simulacro no necesita implementar una Dependency completa, solo el mínimo (utilizado por caller.cpp) para que la prueba se pueda compilar y ejecutar. De esta manera puede simular funciones no virtuales, estáticas, globales o casi cualquier dependencia sin cambiar el código productivo. Otra razón por la que me gusta este enfoque es que todo lo relacionado con la prueba está en un solo lugar. No tienes que modificar las configuraciones del compilador y del enlazador aquí y allá.

He aplicado esta técnica con éxito en un proyecto del mundo real con grandes dependencias grandes. Lo he descrito con más detalle en Incluir simulacro .


Solía ​​crear una interfaz para las partes que necesitaba para burlarme. Luego simplemente creé una clase de código auxiliar derivada de esta interfaz y pasé esta instancia a mis clases en prueba. Sí, es un montón de trabajo duro, pero me pareció que vale la pena por algunas circunstancias.

Oh, por interfaz me refiero a una struct con solo métodos virtuales puros. ¡Nada más!


Una forma que usamos a veces es dividir el archivo .cpp original en al menos dos partes.

Entonces el aparato de prueba puede suministrar sus propias implementaciones; Usando efectivamente el enlazador para hacer el trabajo sucio por nosotros.

Esto se llama " Enlace de costura " en algunos círculos.


Usted dice específicamente "si no puede modificar el código original", lo que hacen las técnicas que menciona en su pregunta (y todas las demás "respuestas" actuales).

Sin cambiar esa fuente, aún puede generalmente (para sistemas operativos / herramientas comunes) precargar un objeto que define su propia versión de la función (es) que desea interceptar. Incluso pueden llamar a las funciones originales después. Proporcioné un ejemplo de cómo hacer esto en (mi) pregunta ¿ Buenas herramientas de monitoreo de TCP / IP de Linux que no necesitan acceso de root? .