Testen von Interna



  • testloser schrieb:

    angenommen ich habe eine Klasse die ein relativ einfaches Interface nach aussen zur Verfuegung stellt, sagen wir zwei Methoden. Interna hat sie aber 20 private Methoden, die relativ aufwaendig und komplex sind.
    Nun möchte ich diese Klasse testen. Imo macht es Sinn (obwohl es sich um BlackBox-Tests handelt) diese 20 Methoden im Unit-Test direkt zu testen.
    Die Frage ist nur wie, schliesslich sind sie private und ich moechte sie auf keinen Fall public machen.

    Das ist IMHO die falsche Einstellung. Wahrscheinlich tut diese Klasse mehr als eine Sache, deshalb sollte sie wahrscheinlich in mehrere Klassen aufgeteilt werden, wobei die jetzt privaten Methoden natürlich zu public-Methoden dieser Teilklassen werden.
    Das hilft der Testbarkeit, Wiederverwendbarkeit und Wartbarkeit, also kurz dem guten Design.

    Außerdem tendieren solche Klassen dazu, ein prozedurales Design unter einer OOP-Oberfläche zu beherbergen. IMHO ist das falsch verstandene Kapselung.

    #define private public
    

    vor dem include des Headers aus der Testdatei. Das ist aber sehr fragwuerdig (und undefiniert?).

    Das ist in der Tat undefiniert.



  • 20 ist natürlich etwas übertrieben, ich hatte gehofft das bringt die Leute davon ab, mir mit Vorschlaegen wie "ist doch unnötig" zu kommen - nun gut dafuer kommt jetzt die Design-Keule 😉

    Das Design der Klasse ist spitze!

    Naja normalerweise wuerde ich auch nicht die privaten Methoden testen wollen, aber im Moment habe ich keinen Debugger zur Hand und da bietet sich ein etwas ausfuehrlicherer Testcode an.

    Naja wirds wohl bleiben muessen.........waere ja auch zu einfach, wenn etwas mal EINFACH und ohne HACKS machbar waere, man.



  • testloser schrieb:

    waere ja auch zu einfach, wenn etwas mal EINFACH und ohne HACKS machbar waere, man.

    Der Sinn von private -Membern besteht ja darin, dass sie den Zugriff von aussen erschweren. Für Tests besteht da keine Ausnahme.

    Also entweder hacken, oder die Klasse ändern und z.B. eine statische Testmethode oder friend einbauen.



  • Nexus schrieb:

    testloser schrieb:

    waere ja auch zu einfach, wenn etwas mal EINFACH und ohne HACKS machbar waere, man.

    Der Sinn von private -Membern besteht ja darin, dass sie den Zugriff von aussen erschweren. Für Tests besteht da keine Ausnahme.

    Das ist nicht ganz richtig. Man markiert Member private, damit niemand sich davon abhängig machen kann, so dass spätere Änderungen ohne Auswirkungen nach außen hin bleiben.
    Da der Test aber immer im Zusammenhang mit der Klasse entwickelt wird, typischerweise von derselben Person, ist eine solche Abhängigkeit überhaupt nicht schlimm, im Gegenteil. Der Test darf und soll angepasst werden müssen, wenn sich die Interna ändern.

    Wie man das löst, weiss ich leider auch nicht. Pauschal ein friend wäre sicher gar keine so schlechte Lösung.



  • Bashar schrieb:

    Nexus schrieb:

    testloser schrieb:

    waere ja auch zu einfach, wenn etwas mal EINFACH und ohne HACKS machbar waere, man.

    Der Sinn von private -Membern besteht ja darin, dass sie den Zugriff von aussen erschweren. Für Tests besteht da keine Ausnahme.

    Das ist nicht ganz richtig. Man markiert Member private, damit niemand sich davon abhängig machen kann, so dass spätere Änderungen ohne Auswirkungen nach außen hin bleiben.
    Da der Test aber immer im Zusammenhang mit der Klasse entwickelt wird, typischerweise von derselben Person, ist eine solche Abhängigkeit überhaupt nicht schlimm, im Gegenteil. Der Test darf und soll angepasst werden müssen, wenn sich die Interna ändern.

    Danke, das meine ich.

    Bashar schrieb:

    Wie man das löst, weiss ich leider auch nicht. Pauschal ein friend wäre sicher gar keine so schlechte Lösung.

    Ja, aber an der Umsetzung haengt es noch bei mir. Ich woellte etwas ganz allgemeines als friend deklarieren und nicht etwa irgendwie eine Basisklasse aus meinem UnitTest-Framework.

    #include <iostream>
    
    class Foo
    {
    public:
      void Bar()
      {
        FooBar();
      }
    
    private:
      friend class TestPrivate;
    
      void FooBar()
      {
        std::cout << "i am private" << std::endl;
      }
    };
    
    class TestPrivate
    {
    };
    
    class TestFoo: public TestPrivate
    {
    public:
      void TestMe()
      {
        foo_.FooBar();
      }
    
    private:
      Foo foo_;
    };
    
    int main()
    {
      TestFoo test_foo;
      test_foo.TestMe();
      return 0;
    }
    

    Bin nicht so fit, was das ganze friend-Verhalten an geht. Wenn ich

    friend class TestPrivate;
    

    durch

    friend class TestFoo;
    

    ersetze geht es trivialer Weise.
    Geht es auch irgendwie unter Nutzung von "TestPrivate"?

    testloser



  • Wozu soll es gut sein, die Interna zu testen? Wichtig ist, dass die Klasse nach außen hin jederzeit richtig funktioniert.



  • Athar schrieb:

    Wichtig ist, dass die Klasse nach außen hin jederzeit richtig funktioniert.

    Ja, und was denkst Du ist mein Ziel? Meinst Du ich schreibe die Tests aus Spass an der Freude?

    Wenn ich testgetrieben entwickle und mit...ich sage mal inneren Methoden, also den kleinsten Bausteinen, anfange, warum sollte ich die nicht testen wollen? Nur weil diese Elemente vllt nicht so allgemeingueltig sind, dass ich sie in eine eigene Datei auslager und damit quasi im ganzen Programm bekannt mache (und damit trivialerweise UnitTests bereitstellen koennte), heisst das doch nicht, dass ich sie deshalb nicht testen sollte.

    Ich wuerde es gern machen und halte es fuer sinnvoll und jetzt bitte bitte bitte wenn dann konstruktives.



  • Den ganzen privaten Kram in eine eigene Klasse auslagern, die kannst du dann Testen. Wenn du jetzt meinst, das ist schlecht und das willst du nicht: das nennt sich PIMPL. Du hast dann noch den Vorteil, dass bei Änderung der Klasse abhängige Units nicht neu kompiliert werden müssen.



  • Eine Klasse bietet eine gewisse Funktionalität an - und zwar über die öffentlichen Methoden. Diese sind es dementsprechend auch, die mit verschiedenen, repräsentativen Eingaben auf korrekte Resultate zu testen sind.
    Die "Interna" sind lediglich Implementierungsdetails. Wenn diese Fehler beinhalten, wird sich dies in den Testresultaten bemerkbar machen.



  • Athar schrieb:

    Die "Interna" sind lediglich Implementierungsdetails. Wenn diese Fehler beinhalten, wird sich dies in den Testresultaten bemerkbar machen.

    Nur wenn die Tests die Fehler der Interna triggern. Je komplizierter das äußere Verhalten, desto unwahrscheinlich ist das.



  • testloser schrieb:

    Wenn ich testgetrieben entwickle und mit...ich sage mal inneren Methoden, also den kleinsten Bausteinen, anfange, warum sollte ich die nicht testen wollen?

    Nach TDD Manier schreibt man das, und nur das, was gerade gebraucht wird. Und das sind die öffentlichen Schnittstellen mit ihren Anforderungen/Vor-Nachbedingungen.
    Du fängst nicht mit den Interna an, sondern mit der Schnittstelle und implementierst nur das allernötigste. Nach und nach erhöhen sich die Anforderungen an deine Schnittstelle. Du refaktorisierst ( 🙄 ) deine Implementierung, abgesichert durch den schon längst bestehenden Test der Schnittstelle.


Anmelden zum Antworten