Frage

Ich bin derzeit auf einer Klasse arbeiten, die eine andere Klasse verwendet, die nur statische Funktionen hat.

Alles funktionierte gut, bis ich versuchte, meine Klasse zu testen.

Hier ist ein einfaches Codebeispiel des Problems:

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!";
        }
    }
}

Unter der Annahme, Klasse A richtig funktioniert (und hat durch seine Unit-Tests getestet), würde Ich mag die runSomething Funktion in der Klasse B überprüfen.

Meine erste Option wäre Mocks für die inneren Klassen erschaffen (In diesem Beispiel - Klasse A), aber in diesem Fall wird es geben Sie mir nichts zu vererben A, weil es nur statische Funktionen.

Meine zweite Möglichkeit ist, die Anrufe für die A-Klasse in privaten Funktionen in B kapseln, damit ich ihre Rückgabewerte kontrollieren kann (obwohl diese Option wählen werde die gut ein wenig komplizierter machen).

Meine Frage an Sie ist: Es gibt in der besseren Möglichkeiten, um Test C ++ Klassen, die als meine aktuellen Optionen auf statische Klassen / Funktionen abhängen

Vielen Dank im Voraus,

Tal.

War es hilfreich?

Lösung

Ich habe Erfolg in Einheit habe in ähnlichen Situationen zu testen, indem die Verweise Refactoring, um die statischen Funktionen (in meinem Fall ist es externe Abhängigkeiten waren) in neue private Funktionen und auf dem getesteten Stummel überschrieben wird, so kann ich diesen Ansatz empfehlen

Wenn die Refactoring-Funktionen privat bleiben, sollten sie nicht stark von der Komplexität des Designs auswirken und sie sollten klein genug sein, nicht zu negativ Auswirkungen auf die Lesbarkeit des Codes.

Andere Tipps

Wenn Sie nicht ein monolithisches Test-Suite verwendet wird, dann ist es einfach. Ich nehme an, dass Sie Klasse A in A.cpp und Klasse B in B.cpp haben, und die Tests für B sind in B_test.cpp.

Erstellen Sie eine Datei mit dem Namen A_mock.cpp

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

Dann, wenn Ihre B_test Datei, nur Link mit A_mock.o statt A.o kompilieren.

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

Sie können einen Zeiger auf die Funktion an den Konstruktor der Klasse A. Dann passieren den Zweck zu testen, Sie einige der Zeiger auf ein Mock-Funktion übergeben können, wo Sie tun können, was Sie wollen.

Warum die statische Funktion? Ich würde vorschlagen, nicht statisch zu machen.

Sie könnten dann eine Schnittstelle für die Klasse A (in C ++ Dies bedeutet eine Klasse mit nur rein virtueller Funktion Header) mit dem Namen AInterface erstellen. Klasse A würde (vererben) AInterface implementieren und diese virtuellen Funktionen implementieren.

Dann passieren einen Zeiger auf diese Schnittstelle in Konstruktor der Klasse B und speichern sie in einer Membervariable namens m_A. Dann in Ihrem Test erstellen MockClassA dass Geräte AInterface. Pass MockClassA in Klasse B Konstruktor und setzen m_A an den Eingang.

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;
}

Prüfregeln

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");
   :
   :
}

Ich würde sagen: „Mann, einige Leute nehmen Einheit Art und Weise testen zu weit!“

So testet die beiden Klassen als eine einzige Einheit. Klasse A ist schwer in der Klasse B codiert sowieso.

Sie sollten die Klasse über Vorlage nehmen, und exportieren ausdrücklich, dass Instantiierung (B<A>) Linker Probleme zu vermeiden, wenn es nicht vorher alle Inline war wie gebucht. Auf diese Weise können Sie andere Klassen für die Zwecke der Prüfung einsetzen, wie Sie benötigen und ist eine gute Praxis sowieso. Ich bin auch neugierig, warum Ihr Beispiel sieht so viel wie Java -. Ich hatte es etwa fünf Mal zu lesen, bevor dass es die Bestimmung tatsächlich beträgt C ++ wie angegeben

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.

Jetzt können Sie eine Mock-Klasse für einen Ersatz, auch wenn Sie nicht von ihm erben können. Infact, dies auch erweiterbar auf andere Weise ist -. CRTP

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

Die Wunder von Vorlagen nie, nie aufhören, mich zu überraschen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top