C++ Unit Tests (schwer zu testbare Klasse)



  • Hallo alle zusamen,
    ich hätte eine Frage bezüglich einem Problem, welches ich zurzeit hab.
    Ich möchte gerne zu einer Klasse, welche schlecht testbar ist, Unit Tests schreiben, welche in dieser Klasse anderer Verhaltensweisen durch ein #define ausführen. Genauer gesagt handelt es sich um eine Klasse, welche Adressen speichert, worauf dann mathematische Operationen wie Alignment, Subtraktion, Addition, etc. ausgeführt werden. Da dies schlecht testbar ist war meine Idee den Variablen mit einem #define UNIT_TEST Konstanten zuzuweisen für Unit Tests.
    Wenn ich nun jedoch ein #define in der main Routine erstelle ist diese nicht im Gültigkeitsbereich der Klasse, welche sich in einer seperaten Klasse befindet. Um euch nicht mit Source Code zu überhäufen, habe ich für mein Problem eine kleine Klasse geschrieben, welche mein Problem veranschaulicht.
    Schon einmal vielen Dank für eure Hilfe 🙂

    Foo.h

    pragma once
    
    #include <iostream>
    
    class Foo
    {
    	private:
    
    	public:
    		void toTest();
    
    };
    

    Foo.h

    #include "Foo.h"
    
    void Foo::toTest()
    {
    #ifndef IS_UNIT_TEST
    	std::cout << "Dies wird ausgegeben bei normaler Ausfuehrung" << std::endl;
    #endif
    
    #ifdef IS_UNIT_TEST
    	std::cout << "Dies wird ausgegeben bei einem unit Test" << std::endl;
    #endif
    }
    

    main.cpp

    // Demo.cpp : Defines the entry point for the console application.
    //
    
    #define IS_UNIT_TEST
    #include "Foo.h"
    
    
    
    int main()
    {
    	Foo foo;
    	foo.toTest();
        return 0;
    }
    

    Gruß Dennis

    -edited: #define vor die #include Anweisung verschoben



  • @Pixma sagte in C++ Unit Tests (schwer zu testbare Klasse):

    Wenn ich nun jedoch ein #define in der main Routine erstelle ist diese nicht im Gültigkeitsbereich der Klasse, welche sich in einer seperaten Klasse befindet.

    what?

    @Pixma sagte in C++ Unit Tests (schwer zu testbare Klasse):

     #define IS_UNIT_TEST
    

    vor

    @Pixma sagte in C++ Unit Tests (schwer zu testbare Klasse):

     #include "Foo.h"
    

    ??



  • oh mein Gott sorry,
    in meinen Hauptprojekt, hab ich es vor der Include Anweisung gehabt.
    Im dem kleinen beispielprojekt hab ich nicht richtig geschaut.
    Ich korrigiere den Source Code grad.



  • @Pixma sagte in C++ Unit Tests (schwer zu testbare Klasse):

    Ich korrigiere den Source Code grad.

    Ich verstehe Dein Problem immer noch nicht.



  • Beim ausführen wird immer die zeile "Dies wird ausgegeben bei normaler Ausfuehrung" ausgegeben.
    Jedoch möchte ich gerne durch das #define IS_UNIT_TEST den programmfluss so ändern, dass die Zeile "Dies wird ausgegeben bei einem unit Test" ausgegeben wird und nicht mehr die andere Zeile.
    Ich hoffe du konntest mein Problem nun verstehen und nochmals sorry wegen dem gravierenden Fehler der mir eben nicht aufgefallen ist.



  • @Pixma sagte in C++ Unit Tests (schwer zu testbare Klasse):

    nochmals sorry wegen dem gravierenden Fehler der mir eben nicht aufgefallen ist.

    Kein Problem. Bin selbst auf der Leitung gestanden vorher. Die Sache ist halt, daß main.cpp und Foo.cpp zwei verschiedene Compilation Units sind. Will heißen:

    main.cpp wird kompiliert und definiert IS_UNIT_TEST, bindet Foo.h ein.
    Foo.cpp wird kompiliert und bindet Foo.h ein. Bekommt aber von IS_UNIT_TEST nichts mit.

    Hm. Womit wird denn das gebaut? Kannst Du IS_UNIT_TEST nicht schon (quasi "global") beim Compileraufruf setzen?

    Plan B:

    testing.h

    #define IS_UNIT_TEST  // oder auch nicht.
    

    Foo.h

    pragma once
    
    #include "testing.h"
    
    class Foo
    {
    	public:
    		void toTest();
    };
    

    Foo.cpp

    #include "Foo.h"
    
    #include <iostream>
    
    void Foo::toTest()
    {
    #ifndef IS_UNIT_TEST
    	std::cout << "Dies wird ausgegeben bei normaler Ausfuehrung\n";
    #elif
    	std::cout << "Dies wird ausgegeben bei einem unit Test.\n";
    #endif
    }
    

    main.cpp

    #include "Foo.h"
    
    int main()
    {
    	Foo foo;
    	foo.toTest();
    }
    

    ... wobei. Den einen Code zu testen der dann garnicht 1:1 in production geht ist schon irgendwie sinnfrei. XY-Problem??

    @Pixma sagte in C++ Unit Tests (schwer zu testbare Klasse):

    eine Klasse, welche Adressen speichert, worauf dann mathematische Operationen wie Alignment, Subtraktion, Addition, etc. ausgeführt werden.

    Vielleicht magst Du das ein bisschen genauer ausführen.



  • Plan A: Ich baue das Projekt mit Visual Studio 2015. Wobei ich die Projektmappe mit Premake5 erstelle.
    Das hatte ich auch schon vorgestern probiert, in dem ich in der premake5.lua im Abschnitt workspace folgende Zeile eingefügt habe: defines { "IS_UNIT_TEST" }. Dies hat aber auch nicht funktioniert.

    Plan B: Wäre eine Überlegung, wenn es keine schönere Möglichkeit gibt. Aber das würde ich gerne als allerletzte Möglichkeit machen.

    Ich schreibe eine Klasse, welche es mir erlaubt an eine Adresse, welche mit "operator new(size);" erstellt wurde eine Variable zu schreiben. Das funktioniert alles auch soweit. Ich kann zusätzliche header (Strukturen), Tracking Strukturen und Boundaries einfügen und Funktionalitäten funktionieren auch alle auf den ersten Blick. Aktuell teste ich das ganze mit dem Visual Studio RAM Analyzer und einem selbstgeschriebenen Log worin die Ausgaben hineingeschrieben werden. Jedoch muss ich da halt manuell kontrollieren. Mein Ziel jedoch ist es diese Komponenten in Form von mehreren Unit Tests automatisiert zu testen, da die Funktionalitäten bereits recht umfangreich sind und ich ungerne alle per Hand immer im Log testen möchte. Später einmal wenn ich mir sicher bin, dass wirklich alles funktioniert kommen die Basiskomponenten wie zum Beispiel ein Linear Allocator, Stack Allocator und ein Pool Allocator.



  • @Pixma sagte in C++ Unit Tests (schwer zu testbare Klasse):

    Ich schreibe eine Klasse, welche es mir erlaubt an eine Adresse, welche mit "operator new(size);" erstellt wurde eine Variable zu schreiben.

    Ich habe keinen Plan wovon Du da redest (und wozu das gut sein soll).

    @Pixma sagte in C++ Unit Tests (schwer zu testbare Klasse):

    Ich baue das Projekt mit Visual Studio 2015.

    Dann IS_UNIT_TEST in [Project Settings] ~> [C/C++] ~> [Preprocessor] ~> [Preprocessor definitions] eintragen und glücklich sein.



  • This post is deleted!


  • hmm,
    wie gesagt, ich entwickel Allocator Klassen (Linear, Stack, ...).

    Das funktioniert in dem Fall leider nicht, da die Klasse in einer anderen Projektmappe ist als meine main function.
    Naja egal, ich teste es dann halt händisch.
    Trotzdem danke 👍



  • Ich nutze auch Unittests. Aber ich mache für die Unittests immer ein eigenes Projekt und teste dort die Module einzeln ab. Ich baue auch keine Compilerweichen ein. Im Zweifel bekommt ein Modul mal noch einen zusätzlichen getter um Informationen oder Stati abzufragen, aber mehr verändere ich nicht.

    Es lässt sich allerdings auch nicht zwangsläufig alles mit Unittests abtesten.



  • Hallo lt0101,

    vielen Dank für deine Antwort. Bei meinen Solution habe ich auch drei Projektmappen.
    Eins für die ganzen Komponenten und dem Memory managern. Diese wird als DLL kompiliert. Ein anderes Projekt noch, welches diese Klassen nutzt, wo auch der Einstiegspunkt sich befindet. Dann hab ich auch noch ein Unit Test Projekt. Hier zu Beschreibung hab ich das gekürzt und umgebaut.

    Hmm ok, dann geht das wohl nicht. Weil im Studium wurde uns beigebracht wie wichtig Unit Tests sind und dass man jede Komponente ausreichend testen soll.



  • Wenn du Makros (#define) von deinem Unit-Test-Projekt aus benutzen willst, dann mußt du selbstverständlich direkt die Sourcen angeben (und kannst nicht die kompilierte Library benutzen).

    Du könntest natürlich auch nur diese Makros in den Header-Dateien nutzen (die du ja zwangsläufig einbinden mußt).

    Aber so wie @It0101 auch geschrieben hat, würde ich auf Makros (explizit für Unit-Tests) verzichten, da du ja sonst nicht den 'Production Code' testest.



  • Die Lösung ist eigentlich (wenn man das überhaupt will ... einen code testen und der andere ist production) ganz einfach und schon genannt. Das Symbol in die Projekteigenschaften eintragen. Wenn einem das bei drei Projects in einer Solution händisch zu viel Arbeit ist, dann gibt es sicher auch andere Wege als sich durch den Properties-Dialog zu klicken. Mein erster Weg wäre zu msbuild.



  • @Pixma sagte in C++ Unit Tests (schwer zu testbare Klasse):

    Weil im Studium wurde uns beigebracht wie wichtig Unit Tests sind und dass man jede Komponente ausreichend testen soll.

    Das ist auch nicht falsch. Aber man sollte im Idealfall beim Design der Komponenten/Module schon berücksichtigen, dass man mit Unittests abtesten will. Einen Unittest über eine wildgewachsene Komponente drüberzustülpen ist selten eine gute Idee. Geht aber auch.

    In dem Moment wo du aber Compilerweichen einbaust, veränderst du den Code. Und durch dumme Umstände ( Morgenkaffee vergessen ) kann es passieren, dass du die eine Sache abtestest und die andere in Production installierst. Und dann nützen dir deine Tests einen Dreck 😉

    Also möglichst unveränderten Quellcode abtesten.



  • habs nun manuell durchlaufen lassen und es funktioniert alles.
    Ich habe jede mögliche Konfiguration der Übergabeparameter kontrolliert und den Log auf die Ergebnisse verglichen.
    Dann verzichte ich in dem Speziallfall auf Unit Tests.

    Vielen Dank für die Hilfe und für den Tipp mit Makros in Bezug auf Unit Tests. 🙂

    Gruß Dennis



  • Ich verstehe das Problem nicht wirklich. Wenn ich das richtig überflogen habe, rufst du deine Funktionalitäten auf und loggst irgendwo die Ergebnisse.
    Dann kannst du doch auch da überprüfen, ob die Ergebnisse die erwarteten sind und fertig ist dein Testfall.


Log in to reply