rai finanzas c++ unit-testing mocking

c++ - rai finanzas



¿Cómo te burlas de las clases que usan RAII en C++? (4)

Aquí está mi problema, me gustaría burlarme de una clase que crea un hilo en la inicialización y lo cierra en la destrucción. No hay ninguna razón para que mi clase falsa realmente cree y cierre hilos. Pero, para burlarse de una clase, he heredado de ella. Cuando creo una nueva instancia de mi clase simulada, se llama al constructor de las clases base, creando el hilo. Cuando se destruye mi objeto simulado, se llama al destructor de las clases base, intentando cerrar el hilo.

¿Cómo se burla una clase de RAII sin tener que lidiar con el recurso real?


En primer lugar, no es necesariamente algo irrazonable que sus clases estén bien diseñadas para su uso, pero mal diseñadas para las pruebas. No todo es fácil de probar.

Presumiblemente, desea utilizar otra función o clase que haga uso de la clase que desea simular (de lo contrario, la solución es trivial). Llamemos al antiguo "Usuario" y al último "Burlado". Aquí hay algunas posibilidades:

  1. Cambiar usuario para usar una versión abstracta de Mocked (puede elegir qué tipo de abstracción utilizar: herencia, devolución de llamada, plantillas, etc. ...).
  2. Compile una versión diferente de Mocked para su código de prueba (por ejemplo, #def el código RAII cuando compila sus pruebas).
  3. Se burló de aceptar una bandera de constructor para desactivar su comportamiento. Yo personalmente evitaría hacer esto.
  4. Simplemente absorba el costo de asignar el recurso.
  5. Omita la prueba.

Los últimos dos pueden ser su único recurso si no puede modificar User o Mocked. Si puede modificar al Usuario y considera que es importante diseñar el código para que pueda probarse, entonces debe explorar la primera opción antes que las demás. Tenga en cuenta que puede haber una compensación entre hacer que su código sea genérico / flexible y mantenerlo simple, ambas cualidades admirables.


En su lugar, crea una interfaz que describe el tipo y hereda de ella tanto la clase real como la clase falsa. Entonces si tuvieras:

class RAIIClass { public: RAIIClass(Foo* f); ~RAIIClass(); bool DoOperation(); private: ... };

Haría una interfaz como:

class MockableInterface { public: MockableInterface(Foo* f); virtual ~MockableInterface(); virtual bool DoOperation() = 0; };

E ir de allí.


Una técnica que he usado es usar alguna forma de decorador. Su código final tiene un método que crea su instancia en la pila y luego llama al mismo método, pero en un miembro que es un puntero a su clase base. Cuando esa llamada regrese, su método regresa destruyendo la instancia que usted creó.

En el momento de la prueba, intercambias un simulacro que no crea ningún subproceso, sino que lo reenvía al método que deseas probar.

class Base{ protected: Base* decorated; public: virtual void method(void)=0; }; class Final: public Base{ void method(void) { Thread athread; decorated->method(); } // I expect Final to do something with athread }; class TestBase: public Base{ void method(void) { decorated->method(); } };


El idioma de los pimpl podría ser adecuado para ti también. Crea tu clase Thread, con una implementación concreta que incluya debajo. Si coloca las #defines y #ifdefs correctas, su implementación puede cambiar cuando habilite las pruebas unitarias, lo que significa que puede cambiar entre una implementación real y una burlada dependiendo de lo que esté tratando de lograr.