Pregunta

Actualmente estoy trabajando en una clase que utiliza otra clase que tiene funciones sólo estáticas.

Todo funcionaba bien hasta que intentara probar mi clase.

Aquí es un simple ejemplo de código del problema:

class A {
    static String getSometing() {
        return String("Something: ") + heavyCalculation().asString();
    }
}

class B {
    B() {}
    ~B() {}
    String runSomething(const String& name) {
        if(name.equals("something")) {
            return A::getSomething();
        } else {
            return "Invalid name!";
        }
    }
}

Suponiendo clase A está funcionando correctamente (y ha sido probado por sus pruebas de unidad), me gustaría para comprobar el runSomething función en la clase B.

Mi primera opción sería la creación de burla para las clases internas (en este ejemplo - clase A), pero en ese caso me dará nada que heredar de una porque tiene funciones sólo estáticas.

Mi segunda opción es encapsular las llamadas para una clase de funciones privadas dentro B para que pudiera controlar sus valores de retorno (aunque la elección de esta opción hará que el bien un poco más complejo).

Mi pregunta es:? ¿Hay en mejores maneras de probar clases de C ++ que depende de las clases / funciones estáticas que mis opciones actuales

Gracias de antemano,

Tal.

¿Fue útil?

Solución

He tenido éxito en la unidad de pruebas en situaciones similares por refactorización a cabo las referencias a las funciones estáticas (en mi caso fue dependencias externas) en nuevas funciones privadas y sobrescribirlos en el talón de la prueba, por lo que puedo recomendar este enfoque

Si las funciones refactorizado se mantengan privadas, que no deben tener repercusiones en gran medida de la complejidad del diseño y que debe ser lo suficientemente pequeño como para no afectar negativamente a la legibilidad del código.

Otros consejos

Si no está utilizando un conjunto de pruebas monolítico, entonces es fácil. Me Suponga que tiene la clase A en a.cpp y la clase B en B.cpp, y las pruebas para B están en B_test.cpp.

Crea un archivo llamado A_mock.cpp

class A
{
    static String getSometing() {
        return String("Expected Something");
    }
};

A continuación, cuando se compila el archivo de B_test, solo vínculo con A_mock.o en lugar de A.o.

g++ -Wall B_test.cpp B.cpp A_mock.cpp

Se puede pasar un puntero a la función al constructor de la clase A. A continuación, para probar, se puede pasar un poco del puntero a una función maqueta donde se puede hacer lo que quiera.

¿Por qué la función estática? Yo sugeriría que no lo que es estático.

A continuación, podría crear una interfaz para la clase A (en C ++ significa esto una clase con cabeceras de funciones virtuales puras solamente) llamado AInterface. Clase A implementaría (hereda) AInterface e implementar estas funciones virtuales.

A continuación, pasar un puntero a esta interfaz en constructor de la clase de B y la almacena en una variable miembro llamada m_A. Luego, en su prueba, crear MockClassA que implementa AInterface. Pass MockClassA en clase B constructor y establecer m_A a la entrada.

class AInterface
{
   virtual String getSomething() = 0;
}

class A : public AInterface
{
    String getSometing() {
        return String("Something: ") + heavyCalculation().asString();
    }
}

class B 
{
    B(AInterface A) :  { m_A = A; }
    ~B() {}
    String runSomething(const String& name) {
        if(name.equals("something")) {
            return m_A.getSomething();
        } else {
            return "Invalid name!";
        }
    }
    AInterface m_A;
}

Código de ensayo

class MockClassA : public AInterface
{
    String getSometing() {
        return String("Whatever I want. This is a test");
    }
}   

void test ()
{
   // "MockClassA" would just be "A" in regular code
   auto instanceOfB = B(MockClassA());

   String returnValue = instanceOfB.runSomething("something");
   :
   :
}

Me decía: "Hombre, algunas personas toman pruebas unitarias demasiado lejos!"

Sólo probar las dos clases como una sola unidad. Clase A está codificado en la clase B de todos modos.

Se debe tomar la clase a través de la plantilla, y exportar explícitamente que la instanciación (B<A>) para evitar problemas de vinculador si no estaba previamente en línea todo lo publicado. De esta manera, se pueden insertar otras clases para los propósitos de la prueba según sea necesario y es una buena práctica de todos modos. También tengo curiosidad por qué su ejemplo se parece tanto a Java -. Tuve que leerlo unas cinco veces antes de determinar que en realidad era C ++ como se indica

template<typename T> class BImpl {
    String runSomething(const String& name) {
        if(name.equals("something")) {
            return T::getSomething();
        } else {
            return "Invalid name!";
        }
    }
};
typedef BImpl<A> B; // Just plugs in to existing code.

Ahora se puede sustituir una clase simulada para A, a pesar de que no se puede heredar de ella. De hecho, ésta es también extensible de otro modo -. CRTP

class A : public BImpl<A> {
    String getSomething() {
        // Now it's non-static! IT'S A MIRACLE!
    }
}

Las maravillas de las plantillas nunca, nunca dejan de sorprenderme.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top