BETA: Test soon: Neues Testing Framework basierend auf Erfahrungen mit anderen Frameworks



  • Umfrage: Bist du mit einem (Unit) Test Framework, das du bisher kennst zufrieden? (Außer "Test soon".)

    Auswahl Stimmen Prozent
    Ja, völlig. 9 30.0%
    Nein, aber ich komme zurecht und möchte auch nicht wechseln. 1 3.3%
    Nein, wurde Zeit für eine neue Alternative! 11 36.7%
    Nein, alles Scheiße, auch "Test soon" ist Müll / muss Müll sein. 9 30.0%

    Wir (Ronny und ich) sind auf der Suche nach Feedback und Anregungen. Daher möchte ich mich auf die Forumsbeschreibung beziehen:

    Ihr sucht ein paar Beta-Tester für eure fertigen Projekte?

    Wobei unser Projekt noch nicht wirklich fertig ist.

    Test soon

    Test soon ist ein Testing Framework zum Beispiel für Unit Tests. Wir haben momentan einen einfachen XML Reporter und einen (genau genommen zwei) Text Reporter für Menschen.

    Was meint ihr? Bitte formuliert eure vernichtende Kritik höflich ;).

    EDIT: Ach ja, das ganze ist für C++. Steht aber auch im Link.



  • es wäre nett, wenn sich die paar gegenstimmen einmal mit kritik äußern würden anstatt nur votes zu setzen



  • Jetzt ist Version 0.58 (zum Zeitpunkt des ersten Posts wars 0.55) raus. Ein direkter Link auf unsere Sourceforge-Projektseite: http://sourceforge.net/projects/testsoon.

    Viel Spaß.



  • Hallo,

    ich habe mir das Framework mal angeschaut (leider nur auf der Homepage, nicht im eigenem Projekt).

    Der Syntax - also das Verwenden von Makros zur Testerzeugung - gefällt mir sehr gut. Ich kenne CPPUnit, CXXUnit und ein paar andere Unit Testing Frameworks für C++ - und keines von denen ist wirklich toll zu benutzen. Test soon sieht da schon wesentlich cooler aus 👍

    Leicht Offtopic:
    Wo ihr gerade ein eigenes Framework entwickelt: Wäre es nicht möglich per Makros Design by Contract zu implementieren? Natürlich nicht zur Laufzeit - Test soon soll aus meinen deklarativen Einschränkungen automatisch Tests erzeugen, beispielsweise so:

    bool is_prime (int number)
    {
         VERIFY (number > 0)
         // .. Berechnung .. ret = ...
         RETURN_AND_VERIFY (ret, ret == 1 || ret == 0)
    }
    

    Was meint ihr 🙂 ?



  • Headhunter schrieb:

    Hallo,

    ich habe mir das Framework mal angeschaut (leider nur auf der Homepage, nicht im eigenem Projekt).

    Der Syntax - also das Verwenden von Makros zur Testerzeugung - gefällt mir sehr gut. Ich kenne CPPUnit, CXXUnit und ein paar andere Unit Testing Frameworks für C++ - und keines von denen ist wirklich toll zu benutzen. Test soon sieht da schon wesentlich cooler aus 👍

    Danke!

    Leicht Offtopic:
    Wo ihr gerade ein eigenes Framework entwickelt: Wäre es nicht möglich per Makros Design by Contract zu implementieren? Natürlich nicht zur Laufzeit - Test soon soll aus meinen deklarativen Einschränkungen automatisch Tests erzeugen, beispielsweise so:

    bool is_prime (int number)
    {
         VERIFY (number > 0)
         // .. Berechnung .. ret = ...
         RETURN_AND_VERIFY (ret, ret == 1 || ret == 0)
    }
    

    Was meint ihr 🙂 ?

    Nicht zur Laufzeit meinst du? Dann wird es zumindest SO nicht möglich sein. So oder so kannst du assert aus <cassert> verwenden. Ich könnte mir mit gewissem technischen Aufwand folgendes vorstellen:

    int strcmp(char const *a, char const *b) {
       VERIFY_RETURN_VALUE(_1 == 0 || _1 == -1 || _1 == 1);
    }
    

    Aber es ist durchaus möglich, den Test im jetzigen Stil direkt nach der Funktion zu schreiben und <testsoon.hpp> mittels des Makros TESTSOON_DUMMY bei Nichtbedarf in einen Dummy-Modus zu setzen, der nur wegoptimierbaren Code generiert.

    PS: Um sicherzustellen, dass number > 0 ist, könntest du übrigens einen eigenen Typen verwenden, aber das nur am Rande.



  • Ihr habt da eine Abhängigkeit zu boost, ist die wirklich nötig?
    Und warum sollte man dann nicht direkt boost::test verwenden?



  • Hi,

    bei meinem Beispiel ging es nicht um Programmierstil, sondern nur um Prinzip 🙂

    Vielleicht sollte ich mal erklären was ich möchte.

    Was möchte man bei Unittests eigentlich erreichen?

    1. überprüfen mit korrekten Werten/Ausgangssituationen
    2. überprüfen mit falschen Werten/Ausgangssituationen
    3. dokumentieren des Quellcodes (gute Beispiele geben, "so geht's nicht"..)
    4. Bugfixing (hier eher sekundär)

    Um all diese Aufgaben zu erledigen bieten sich Unittests an. Sie haben jedoch einen Nachteil: Jeder Test muss außerhalb der Implementierung manuell geschrieben werden. Wenn ich also eine Klasse "foo" erzeuge, muss ich daran denken einen Test namens "test_foo" zu schreiben.

    Eine Design by Contract Testlösung hätte dieses Problem nicht - schon beim Code tippen sehe ich, welche Eingangsbeschränkungen eine Funktion hat. Die dazu automatisch generierten Testfunktionen sollen die Funktion dann jeweils mit (automatisch generierten) falschen und korrekten Eingabewerten durchtesten.
    Wenn gewünscht kann der Testcode dann von mir editiert werden.

    Vom Prinzip ähnelt das dem Scaffolding aus Rails: Anhand von vorgegebenen Objekten (bei Rails Tabellen) wird dazu passender Code (HTML+Ruby) erzeugt. Sehr agil 🙂

    Wie man das jedoch implementieren kann, weiß ich nicht.
    Wahrscheinlich muss man die Sources mit nem eigenem Preprozessor bearbeiten.
    Alternativ: Wann werden die Konstruktoren von lokalen statischen Objekten aufgerufen? Wenn die ctors alle beim Start eines Programmes aufgerufen werden, hat man gewonnen. Gibt's da vielleicht nen GCC-Switch für? Mal schauen..



  • phlox81 schrieb:

    Ihr habt da eine Abhängigkeit zu boost, ist die wirklich nötig?
    Und warum sollte man dann nicht direkt boost::test verwenden?

    Boost brauchen die zwei für die Named Parameters. Ob man die braucht ist ne andere Frage, aber boost ist eigentlich immer praktisch.



  • phlox81 schrieb:

    Ihr habt da eine Abhängigkeit zu boost, ist die wirklich nötig?
    Und warum sollte man dann nicht direkt boost::test verwenden?

    Wir machen exzessiv gebrauch von Boost.Preprocessor. Das hat nur Boost. Und wir brauchen es dafür. Du brauchst allerdings wirklich nur die angegebenen Teile von Boost. Boost.Asssign könnte man optional machen.

    Warum nicht Boost.Test? Weil mir das genausowenig wie die anderen gefällt. IMHO Grund genug.

    EDIT: Die named parameter sind ein integraler Teil von Test soon. Nur so können wir die nötige Flexibilität erreichen und trotzdem gute Syntax haben. Wie sonst ginge sowas? (Check1 ist ein generischer Check auf einen unären Funktor)

    XTEST((name, "dummy test") (range, (int, 0, 7))) {
      Check1(something, value);
    }
    

    EDIT 2: schließende Klammer hat gefehlt (die Klammern sind technisch nötig)



  • Headhunter schrieb:

    Hi,

    bei meinem Beispiel ging es nicht um Programmierstil, sondern nur um Prinzip 🙂

    Mir gehts um Machbarkeit. OHNE extra Präprozessortool (welches nicht cpp heißt :D).

    Headhunter schrieb:

    Vielleicht sollte ich mal erklären was ich möchte.

    Was möchte man bei Unittests eigentlich erreichen?

    1. überprüfen mit korrekten Werten/Ausgangssituationen
    2. überprüfen mit falschen Werten/Ausgangssituationen
    3. dokumentieren des Quellcodes (gute Beispiele geben, "so geht's nicht"..)
    4. Bugfixing (hier eher sekundär)

    Auf jeden Fall, allerdings haben Punkte 1 und 2 Priorität.

    Headhunter schrieb:

    Um all diese Aufgaben zu erledigen bieten sich Unittests an. Sie haben jedoch einen Nachteil: Jeder Test muss außerhalb der Implementierung manuell geschrieben werden. Wenn ich also eine Klasse "foo" erzeuge, muss ich daran denken einen Test namens "test_foo" zu schreiben.

    Unsere Tests brauchen keinen Namen. Ach und ich weiß nicht ob ich die Bezeichnung "Unit Test" mag. Oder um mit Helge Schneider zu sprechen: "Das ist mir zu profan." 🤡

    Headhunter schrieb:

    Eine Design by Contract ...

    *quietsch* *halt*

    Das solltest du genauer erklären. Was meinst du? Wozu brauchst du das? Ist es wirklich vorteilhaft?

    Der Wikipedia-Artikel ist nicht lesbar.

    Dann: Lässt sich das auch anders lösen? Kannst du deine Constraints nicht ins Typsystem einbauen? Du brauchst einen Wert > 0? Du nimmst (unsigned. Oder aber) checked integer.

    Dann: Sind Test soon / Unit tests nicht trotzdem sinnvoll? Lässt sich alles über einen Vertrag prüfen?

    Dann: Das mit dem "dran denken" gilt hier auch.

    Headhunter schrieb:

    ... Testlösung hätte dieses Problem nicht - schon beim Code tippen sehe ich, welche Eingangsbeschränkungen eine Funktion hat. Die dazu automatisch generierten Testfunktionen sollen die Funktion dann jeweils mit (automatisch generierten) falschen und korrekten Eingabewerten durchtesten.
    Wenn gewünscht kann der Testcode dann von mir editiert werden.

    Wenn du konkretere Ideen hast, kannst du dich gerne mit uns zusammen setzen und dann überlegen wir, was Sinn macht. Deine Idee ist reizvoll, scheint aber nicht unbedingt praktikabel.

    Headhunter schrieb:

    Vom Prinzip ähnelt das dem Scaffolding aus Rails: Anhand von vorgegebenen Objekten (bei Rails Tabellen) wird dazu passender Code (HTML+Ruby) erzeugt. Sehr agil 🙂

    *flycht* Ronny nervt mich auch ständig mit diesen Buzzwords. Muss das sein?

    Headhunter schrieb:

    Wie man das jedoch implementieren kann, weiß ich nicht.
    Wahrscheinlich muss man die Sources mit nem eigenem Preprozessor bearbeiten.
    Alternativ: Wann werden die Konstruktoren von lokalen statischen Objekten aufgerufen? Wenn die ctors alle beim Start eines Programmes aufgerufen werden, hat man gewonnen. Gibt's da vielleicht nen GCC-Switch für? Mal schauen..

    Daran habe ich gedacht, als ich von "mit einigem Aufwand machbar" sprach.

    Prost.

    PS: BB-Code ist voll scheiße.



  • Hi,

    bei Unittesting (um mal bei dem Wort zu bleiben) geht es mir nicht um Stil oder Schönheit, sondern um Praktikabilität. Wenn ich was schönes elegantes will schreibe ich Code oder entwickle ein tolles System. Unittests müssen funktionieren , umfassend testen und das am Besten ohne großen Entwickleraufwand. Deshalb finde ich das Prinzip von Test soon auch so toll - simplicity rules!

    Zum Design by Contract und zu Entscheidungen ins Typsystem verlagern:

    Was ist ein unsigned int?
    Im Prinzip ist es ein normaler int mit der Constraint "n>0". Für ein char gilt "c>=0 && c<256". Das sind Einschränkungen, nur nennen wir sie hier nicht Constraints sondern Typen.
    Das schöne an Typen ist: Sie sind eingebaut und für den Entwickler bindend. Ich kann keinen uint mit einem negativen Wert initialisieren.

    Meine Funktionen un Klassen haben auch Constraints. So hat die Funktion "getValueFromPointer(foo* bar)" die Einschränkung, dass "bar" != NULL sein muss.
    Bei einer Funktion "getFileModificationDate(string path)" ist die Einschränkung, dass "path" eine gültige Datei beschreibt. Diese Einschränkungen gelten erstmal implizit durch einen entsprechenden Headercomment. Zusätzlich schreibt man noch ein Fehlerüberprüfungs-if an den Anfang der Funktion, um fehlerhafte Eingaben auszuschließen.
    Dann schreibt man Unit Tests, um dieses Verhalten zu verifizieren und zu dokumentieren. Der Unit Test sagt also was richtig und was falsch sein soll.
    Problem auch hier: Sowas ist implizit: Es greift nur *wenn* ich Tests für alle relevanten Teile schreibe (Code Coverage) und *wenn* ich die Tests ausführe.

    Ein Design by Contract bringt diese Überprüfungen bzw. Funktionsspezifierungen in den Quellcode und ist so ein integraler Bestandteil der Entwicklung. Allein diese Tatsache macht Design by Contract schonmal wertvoller als Unit Tests.

    Was heißt das jetzt in drei Sätzen?
    * Simple is beautiful, komplexe Sachverhalte auf einfache Bausteine reduzieren rockt.
    * Design by Contract ist ein simples Prinzip mit simplen Werkzeugen, das komplexe Sachverhalte einfach sichtbar macht.
    * Unit Tests überfordern viele Entwickler, da sie kein integraler Bestandteil eines Programmes sind, das Programm startet auch ohne grünes Lämpchen - DbC setzt da die Daumenschraube an und forciert Spezifikationen.



  • Headhunter schrieb:

    Meine Funktionen un Klassen haben auch Constraints. So hat die Funktion "getValueFromPointer(foo* bar)" die Einschränkung, dass "bar" != NULL sein muss.

    Also das lässt sich in C++ recht problemlos lösen.

    template<class T>
    class non_null_ptr {
      T *p;
    
    public:
      non_null_ptr(T *p) : p(p) {
        if (p == 0)
          throw std::foo_exception();
      }
    
      T &operator*() { return *p; }
      ...
    };
    

    Headhunter schrieb:

    Bei einer Funktion "getFileModificationDate(string path)" ist die Einschränkung, dass "path" eine gültige Datei beschreibt.

    Du meinst natürlich einen gültigen Dateinamen. Alles weitergehende wäre eine race condition... Naja, auch das lässt sich analog lösen.

    Headhunter schrieb:

    Diese Einschränkungen gelten erstmal implizit durch einen entsprechenden Headercomment. Zusätzlich schreibt man noch ein Fehlerüberprüfungs-if an den Anfang der Funktion, um fehlerhafte Eingaben auszuschließen.

    Jo, oder ein assert.

    Headhunter schrieb:

    Dann schreibt man Unit Tests, um dieses Verhalten zu verifizieren und zu dokumentieren. Der Unit Test sagt also was richtig und was falsch sein soll.
    Problem auch hier: Sowas ist implizit: Es greift nur *wenn* ich Tests für alle relevanten Teile schreibe (Code Coverage) und *wenn* ich die Tests ausführe.

    Unit tests sind für andere Dinge wichtiger und durch DbC nicht vollständig zu ersetzen. Das möchte ich in den Raum gestellt wissen.

    Headhunter schrieb:

    Ein Design by Contract bringt diese Überprüfungen bzw. Funktionsspezifierungen in den Quellcode und ist so ein integraler Bestandteil der Entwicklung. Allein diese Tatsache macht Design by Contract schonmal wertvoller als Unit Tests.

    *hüstel* Die Anwendungsfelder sind wie gesagt nicht deckungsgleich. Darüber hinaus werden komplexere Spezifikationen schnell fehleranfällig.

    Auch Unit tests sind integraler Anteil der Entwicklung. Ich entwickle ein Modul und nach jeder Veränderung lasse ich alle Test laufen bzw. füge auch neue hinzu. Natürlich gibt es auch Leute, die das als "Buzzword" einfach ablehnen.

    Headhunter schrieb:

    Was heißt das jetzt in drei Sätzen?
    * Simple is beautiful, komplexe Sachverhalte auf einfache Bausteine reduzieren rockt.
    * Design by Contract ist ein simples Prinzip mit simplen Werkzeugen, das komplexe Sachverhalte einfach sichtbar macht.
    * Unit Tests überfordern viele Entwickler, da sie kein integraler Bestandteil eines Programmes sind, das Programm startet auch ohne grünes Lämpchen - DbC setzt da die Daumenschraube an und forciert Spezifikationen.

    Das dritte ist interessant. Aber mit Programmiererüberforderung kann ich halt nichts anfangen. Sonst wäre ich ja kein Java-Gegner. 🤡

    Meine Bitte: Gib so simplen Ansätzen wie "Unit" Testing oder halt "Test soon" trotzdem auch in der Praxis eine Chance.



  • Hallo,

    versteh mich nicht falsch MrN - ich betrachte Unit Tests durchaus als ein wichtiges Element in der Softwareentwicklung. Aber es ist nicht der Weisheit letzter Schluss (aus genannten Gründen), DbC würde es prima ergänzen.
    Den NULL-sicheren Pointer in C++ finde ich grausam, sieht echt schlimm aus 🙂



  • Headhunter schrieb:

    Hallo,

    versteh mich nicht falsch MrN - ich betrachte Unit Tests durchaus als ein wichtiges Element in der Softwareentwicklung. Aber es ist nicht der Weisheit letzter Schluss (aus genannten Gründen), DbC würde es prima ergänzen.

    OK. Ist nur vielleicht ein wenig missverständlich wenn du hier anfängst DbC zu preisen :p.

    Headhunter schrieb:

    Den NULL-sicheren Pointer in C++ finde ich grausam, sieht echt schlimm aus 🙂

    Wieso? Ich find den toll. Gut, benutzen tu ich das nicht :D.



  • Es macht einen recht kompakten Eindruck; das gefällt mir schon mal gut! 🙂

    Aber

    Headhunter schrieb:

    phlox81 schrieb:

    Ihr habt da eine Abhängigkeit zu boost, ist die wirklich nötig?
    Und warum sollte man dann nicht direkt boost::test verwenden?

    Boost brauchen die zwei für die Named Parameters. Ob man die braucht ist ne andere Frage, aber boost ist eigentlich immer praktisch.

    Boost ist im Vergleich dazu dann der Aufwand-Overkill für die Entwicklungsinfrastruktur! 😞

    Nichts gegen Boost; aber ich habe ein Projekt (und es ist nicht das erste) bei dem ich Boost fürs Release nicht einsetzen darf (ist halt so...).
    Da würde der Aufwand für die Boost-Installation nicht amortisieren.

    Wenn's auch ohne Boost kompiliert werde ich's gerne mal testen.

    Weiter so!

    Grüsse && Happy Hacking !

    *this



  • Gast++ schrieb:

    Es macht einen recht kompakten Eindruck; das gefällt mir schon mal gut! 🙂

    Aber

    Headhunter schrieb:

    phlox81 schrieb:

    Ihr habt da eine Abhängigkeit zu boost, ist die wirklich nötig?
    Und warum sollte man dann nicht direkt boost::test verwenden?

    Boost brauchen die zwei für die Named Parameters. Ob man die braucht ist ne andere Frage, aber boost ist eigentlich immer praktisch.

    Boost ist im Vergleich dazu dann der Aufwand-Overkill für die Entwicklungsinfrastruktur! 😞

    Nichts gegen Boost; aber ich habe ein Projekt (und es ist nicht das erste) bei dem ich Boost fürs Release nicht einsetzen darf (ist halt so...).
    Da würde der Aufwand für die Boost-Installation nicht amortisieren.

    Wenn's auch ohne Boost kompiliert werde ich's gerne mal testen.

    Weswegen darfst du es nicht einsetzen?
    Die Boost Lizenz ist doch recht liberal...



  • phlox81 schrieb:

    Weswegen darfst du es nicht einsetzen?
    Die Boost Lizenz ist doch recht liberal...

    Zu dem Projekt hatte ich schon mal etwas geschrieben:
    (leider ist das von einem Moderator zu "günstig" umplaziert worden)

    http://www.c-plusplus.net/forum/viewtopic-var-t-is-181214.html

    Kurzgefasst ist der Verzicht auf Boost u.a. eine Vorgabe der Konzern-IT.

    Grüsse

    *this



  • Gast++ schrieb:

    Wenn's auch ohne Boost kompiliert werde ich's gerne mal testen.

    Keine Chance.

    (Durch Zufall ist mir aufgefallen, dass jemand "vor kurzem" geantwortet hat.)





  • Changelog?


Anmelden zum Antworten