Cómo usar objetos simulados [C ++] sin pasarlos como argumentos a funciones

7

Estoy en el proceso de integrar un Framework de Pruebas Unitarias para una base de código existente en C ++. Me he centrado en CxxTest, pero resulta que también podemos usar otros Mocking Frameworks (como googlemock) junto con CxxTest.

Después de leer los tutoriales sobre CxxTest (Mocking) y googlemock (el ejemplo de la infame tortuga), tengo la idea general de que tienes que definir una clase simulada (usando macros, etc.), declarar un objeto de la clase simulada y luego pasarlo. a la función usted es la prueba de unidad. Ahora, hay muchos casos en la base de código existente donde no es posible hacerlo.

Aquí hay un pseudo ejemplo para aclarar:

class CCandidateForTest
{
    public:
    bool foo(int a)
    {
        CAnotherClass obj;
        int b = obj.bar(a+2);
        if (a == b) {
            return true;
        } else {
            return false;
        }
    }
}

(Esto está demasiado simplificado, también hay excepciones y los tipos primitivos también pueden ser otros objetos, etc., pero se tiene la idea general) (también, la creación de objetos no siempre es directa y algunas veces puede usar una fábrica)

Quiero escribir una prueba para la función CCandidateForTest::foo . Este método crea internamente un objeto de CAnotherClass , que necesito simular para que CAnotherClass::bar devuelva diferentes valores, de modo que las diferentes rutas de código en foo sean recorridas en las pruebas unitarias.

En pocas palabras, el problema es que la función que se está probando internamente crea un objeto de la clase que necesita ser burlado, por lo que no es posible pasar una instancia del objeto simulado a la función.

¿Cómo uso la burla en tal caso? ¿Existe un marco de burla específico que lo haga posible?

    
pregunta dotbugfix 16.01.2014 - 10:26

4 respuestas

3

Puedes usar una fábrica para crear instancias de CAnotherClass .

class CCandidateForTest
{
    public:
    bool foo(int a)
    {
        std::shared_ptr< CAnotherClassInterface > obj = CAnotherClassFactory::Create();
        int b = obj->bar(a+2);
        if (a == b) {
            return true;
        } else {
            return false;
        }
    }
}

CAnotherClassFactory puede verse como:

class CAnotherClassFactory
{
public:
    typedef std::shared_ptr< CAnotherClassInterface > (*fn)();
    static std::shared_ptr< CAnotherClassInterface > Create()
    {
        return createFn();
    }

    static fn createFn;
};

Pensamientos aleatorios sobre tu enfoque:

  • Si eso está demasiado simplificado, entonces debes pensar cómo refactorizarlo.
  • hay mejores marcos de prueba de unidad que cxxunit. gtest y cute-test.com
respondido por el BЈовић 16.01.2014 - 11:26
4

Usa una fábrica. Haga que la clase candidata tome la fábrica en el constructor. Se burlan de la fábrica para su prueba. Puede usar una biblioteca de simulacros para eso o simplemente usar implementos de prueba y de prueba separados de una clase de fábrica abstracta pura.

    
respondido por el Alexander Torstling 16.01.2014 - 13:11
3

Si una clase crea internamente objetos para hacer su trabajo, entonces ese objeto no es un "colaborador" en absoluto. Para propósitos de prueba, esta es exactamente la misma situación que si tuviera una gran cantidad de código en línea haciendo lo mismo. En otras palabras, no puede burlarse de este objeto porque, desde la perspectiva que debe asumir la prueba, ¡no existe!

(Esto puede ser un indicador de que tu clase está haciendo demasiado, y puede ser un argumento al ponderar si se debe refactorizar la clase para que tenga un verdadero colaborador, pero es solo uno factor: puede haber otras buenas razones para dejar la clase sin cambios y probar su comportamiento lo mejor posible sin burlarse.)

    
respondido por el Kilian Foth 16.01.2014 - 10:32
-2

Vi que teníamos una función que generó / creó internamente su propio objeto para manipularlo. Desde mi punto de vista, no es un código realmente comprobable. Por lo tanto, si intentamos hacer una prueba unitaria para él, es posible que no refleje el verdadero valor de TDD aquí. Además, puede hacer que el código de prueba de tu unidad y los objetos simulados sean más complejos. En general, deberíamos tener un código de refactor antes de hacer cualquier UT entonces. Podemos hacer alguna forma de refactorizar la implementación, como: pasar más argumentos para que sea más compatible con el código comprobable, usar el patrón de fábrica / función global para generar un objeto ...

    
respondido por el kansaz 11.01.2017 - 10:52

Lea otras preguntas en las etiquetas