6 Stimmen

Fälschungsimplmentierungen in C++

Ich brauche eine Mock-Implementierung einer Klasse - zu Testzwecken - und frage mich, wie ich am besten vorgehen soll. Ich kann mir zwei allgemeine Möglichkeiten vorstellen:

  1. Erstellen Sie ein Interface, das alle öffentlichen Funktionen der Klasse als reine virtuelle Funktionen enthält, und erstellen Sie dann eine Mock-Klasse, die davon abgeleitet ist.
  2. Markieren Sie alle Funktionen (nun, zumindest alle, die gemockt werden sollen) als virtual.

Ich bin es gewohnt, es auf die erste Weise in Java zu machen, und das ist auch ziemlich häufig (wahrscheinlich, da sie einen dedizierten Interface-Typ haben). Aber ich habe solche interface-lastigen Designs in C++ kaum gesehen, daher frage ich mich.

Die zweite Methode wird wahrscheinlich funktionieren, aber ich kann nicht anders, als sie irgendwie hässlich zu finden. Macht das irgendjemand?

Wenn ich dem ersten Weg folge, brauche ich etwas Unterstützung bei der Namensgebung. Ich habe ein Audiosystem, das für das Laden von Sounddateien und das Abspielen der geladenen Tracks verantwortlich ist. Ich verwende dafür OpenAL, daher habe ich das Interface "Audio" und die Implementierung "OpenALAudio" genannt. Dies impliziert jedoch, dass der ganze OpenAL-spezifische Code in diese Klasse muss, was sich irgendwie einschränkend anfühlt. Eine Alternative wäre, den Namen der Klasse "Audio" zu belassen und einen anderen für das Interface zu finden, z.B. "AudioInterface" oder "IAudio". Was würden Sie vorschlagen, und warum?

4voto

SingleShot Punkte 18270

Genau wie ich Mock-Objekte in Java nicht von Hand erstellen würde, würde ich sie auch nicht von Hand in C++ erstellen. Mock-Objekte sind nicht nur stubbed-Klassen, sondern sind Testwerkzeuge, die automatisierte Checks durchführen, wie z.B. die Überprüfung, ob bestimmte Methoden aufgerufen werden oder ob sie in der richtigen Reihenfolge aufgerufen werden usw. Ich würde mir die verschiedenen Mock-Objekt-Frameworks für C++ ansehen. googlemock sieht interessant aus, aber es gibt auch andere.

Hinsichtlich der Abstrahierung des Konzepts der Steuerung von Audio-Ressourcen von der Implementierung bevorzuge ich auf jeden Fall die Verwendung eines C++ "Interfaces" (reine virtuelle Basisklasse) mit einem generischen Namen (z.B. Audio) und einer Implementierungsklasse, die nach dem benannt ist, was sie besonders macht (z.B. OpenALAudio). Ich rate davon ab, das Wort "Interface" oder "I" in die Klassennamen einzugliedern. Das Einfügen von Typen oder programmatischen Konzepten in Namen ist seit vielen Jahren aus der Mode gekommen (und kann zu einer weit verbreiteten Umbenennung führen, wenn Sie beispielsweise ein "Interface" zu einer vollwertigen "Klasse" erheben).

Die Entwicklung zu Schnittstellen ist ein objektorientiertes Konzept und daher für C++ geeignet. Einige der wichtigsten Bücher zum Design, die speziell auf C++ abzielen, befassen sich alle mit dem Programmieren zu Schnittstellen (was in C++-Begriffen bedeutet, dass man mit reinen virtuellen Basisklassen programmiert). Zum Beispiel Design Patterns und Large Scale C++ Software Design.

3voto

Stephane Rolland Punkte 36818

"Aber ich habe selten so interface-lastige Designs in C ++ gesehen", zur Information empfehle ich Ihnen, sich einfach den Microsoft COM-Ansatz anzusehen. Diese Technologie, basierend auf C ++, dreht sich alles um interface-lastiges Design.

Design durch Schnittstelle ist eine gute Art zu programmieren. Wenn Sie daran gewöhnt sind, fahren Sie so fort.

Wenn Sie sich in Bezug auf Namen eingeschränkt fühlen, ist es eine gute Praxis, Namensräume zu verwenden. Und für einen Schnittstellennamen ist es üblich, sie ISomething zu nennen, also nennen Sie sie einfach IAudio.

0voto

Ich würde sagen, das hängt sehr von der konkreten Situation und der erforderlichen Komplexität des Musters ab. Ich gehe davon aus, dass Sie idealerweise das Original verwenden würden, aber dies aufgrund komplexer Abhängigkeiten vermeiden möchten. Dann könnte es sich lohnen, den Header zu kopieren und alles auszukommentieren, und die Mock-Funktionalität nach Bedarf wieder zu aktivieren, während Sie mit der Entwicklung Ihres Testcodes fortfahren.

Ich muss zugeben, dass ich keine praktische Erfahrung damit habe, aber es scheint mir, dass je weniger invasiv Sie in Ihrem Originalcode sein können, desto besser.

0voto

Sean DeNigris Punkte 6164

Haben Sie in Betracht gezogen, ein Mock-Framework wie Hippo Mocks zu verwenden?

Von der Wiki:

class Foo {
private:
    IBar *bar;
public:
    Foo(IBar *bar);
    int a(); //ruft IBar::c auf
};

class IBar {
public:
    virtual ~IBar() {}
    virtual void b() = 0;
    virtual int c(std::string) = 0;
};

void TestAFunctionInFoo() {
    MockRepository mocks;
    IBar *barMock = mocks.InterfaceMock();
    Foo *newFoo = new Foo(barMock);
    mocks.ExpectCall(barMock, IBar::c).With("hello").Return(42);
    newFoo->a();
    delete newFoo;
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X