2016-07-27 9 views
2

Ich lese mehrere Artikel über Mocking C-Funktionen (wie CMock oder CMocka), aber ich bin mir nicht sicher, wie die tatsächlichen Funktionen durch mokierte Funktionen in diesem Prozess ersetzt werden. Zum Beispiel beruht CMocka auf automatischem Wrapping mit einem GNU-Compiler, der Parameter wie --wrap unterstützt, um das Präfix __wrap an Funktionsaufrufe anzuhängen, oder schwache Symbole, mit denen Sie jedes beliebige Symbol überschreiben können.Mocking C-Funktionen in MSVC (Visual Studio)

Aber wie macht man das in Visual Studio, für so ziemlich alle anderen Frameworks?

Zum Beispiel CMock has an example diesem ähnlich ist (vereinfacht viel hier):

// myfunc.c 
#include <parsestuff.h> 

// this is the function we would like to test 
int MyFunc(char* Command) 
{ 
    // this is the call to the function we will mock 
    return ParseStuff(Command); 
} 

Es gibt auch die tatsächliche Umsetzung, die die eigentliche Funktion enthält der Linker in der tatsächlichen Anwendung finden sollte:

// parsestuff.c 

int ParseStuff(char* cmd) 
{ 
    // do some actual work 
    return 42; 
} 
Jetzt

, während die Prüfung der Ruby-Skript erstellt Mock-Funktionen wie:

// MockParseStuff.c (auto created by cmock) 

int ParseStuff(char* Cmd); 
void ParseStuff_ExpectAndReturn(char* Cmd, int toReturn); 
  1. Wenn das VS-Projekt bereits parsestuff.c enthält, wie wird es dann möglich sein, dass der Anruf von myfunc.c in MockParseStuff.c endet?

  2. Bedeutet das, dass ich nicht parsestuff.c im Unit-Test-Projekt enthalten kann? Aber wenn das der Fall ist, dann ist es auch unmöglich, zum Beispiel MyFunc von myfunc.c in irgendwelchen Tests zu verspotten, da ich bereits die Datei es einschließen musste, um es zu prüfen?

(Update) Ich bin mir auch bewusst, dass ich die .c Datei anstelle der .h Datei enthalten kann, und dann einige Präprozessor Sachen tun, um den ursprünglichen Anruf zu ersetzen, wie:

// replace ParseStuff with ParseStuff_wrap 
#define ParseStuff ParseStuff_wrap 
// include the source instead of the header 
#include <myfunc.c> 
#undef ParseStuff 

int ParseStuff_wrap(char* cmd) 
{ 
    // this will get called from MyFunc, 
    // which is now statically included 
} 

aber das scheint eine Menge Klempnerei zu sein, und ich sehe es nirgendwo erwähnt.

+0

[Diese Frage] (http://StackOverflow.com/q/33790425/1488067) fragte die spezifische Sache über das Konfigurieren des Linkers, um die Funktionen zu umhüllen, aber ich glaube, dass es in MSVC nicht möglich ist. Auch CMocka erwähnt dies, aber CMock erwähnt diese Anforderung überhaupt nicht. – Lou

+0

Müssen Sie C-Style-Funktionen in einer C++ - Umgebung vortäuschen oder haben Sie nur C-Umgebung? Wenn Sie C++ verwenden können, gibt es eine sehr einfache Lösung für Ihr Problem, und ich kann ein Beispiel geben. – mrAtari

+0

@mrAtari: es ist Visual Studio, also kann ich C++ und C mischen, aber es könnte Probleme mit bestimmten C-Code geben, der nicht mit C++ kompatibel ist ([ähnliche Frage] (http://softwarerecs.stackexchange.com/q/) 34878/22983), zum Beispiel). Wenn es jedoch die meiste Zeit funktioniert, stellen Sie bitte trotzdem ein Beispiel vor, wie C++ das Problem lösen könnte - insbesondere, wenn Sie vermeiden können, dass testbedingte Präprozessordirektiven in den Produktionscode eingefügt werden. – Lou

Antwort

1

Hier ist eine einfache und kurze Lösung mit hippomocks:

Ich habe eine leere Win32-Konsolenanwendung mit

  • main.cpp
  • myfunc.c + myfunc.h
  • parsestuff.c , parstestuff.h

und fügte den Code aus Ihrem Beispiel hinzu.

Mit Hilfe von Hippomocks können Sie jede C-Funktion vortäuschen. Hier ist, wie meine main.cpp sieht so aus:

#include "stdafx.h" 
#include "myfunc.h" 
#include "hippomocks.h" 


extern "C" int ParseStuff(char* cmd); 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    MockRepository mocks; 

    mocks.ExpectCallFunc(ParseStuff).Return(4711); 

    char buf[10] = ""; 

    int result = MyFunc(buf); 

    return result; //assert result is 4711 
} 

HippoMocks ist eine kostenlose, einfach und sehr leistungsfähige Ein-Header-Framework und kann auf GitHub heruntergeladen werden.

Hoffnung Ich habe die Prämie verdient :)

UPDATE Wie es funktioniert:

  1. HippoMocks erhält den func Zeiger auf ParseStuff
  2. HippoMocks baut einen Ersatz func Zeiger auf eine Vorlage Funktion mit gleicher Signatur und eigener Implementierung.
  3. Hippomocks patcht den JMP-Opcode aus dem Funktionsaufruf-Prolog im Speicher, so dass er auf die ersetzte Funktion zeigt.
  4. Ersatz und Speicher Patch werden nach Aufruf oder in Destruktor freigegeben.

Hier ist, wie es aus wie auf meiner Maschine aussieht:

@ILT+3080(_ParseStuff): 
00D21C0D jmp HippoMocks::mockFuncs<char,int>::static_expectation1<0,char *> (0D21DB1h) 

Wenn Sie die Speicheradresse 00D21C0D sehen im Speicher-Fenster (von Lauf unterscheiden laufen), werden Sie sehen, dass es nach gepatcht wird der Aufruf von ExpectCallFunc.

+0

Danke, ich werde es gleich überprüfen, aber kannst du den Mechanismus dahinter erklären? Wie wird der Aufruf von 'ParseStuff'in' myfunc.c' während der Link-Phase umgeleitet? Nun, ich werde mir auch den Quellcode ansehen, aber ich denke, es wäre wertvoll, diese Informationen zu haben. :) – Lou

+0

[Heilige Mutter des Herrn, was ist das Zauberei?] (Https://github.com/dascandy/hippomocks/blob/master/HippoMocks/hippomocks.h) O_o – Lou

+0

@Lousy: Der Typ, der es geschrieben hat, war ein echter Zauberer. :))) – mrAtari

0

Ich habe mich nicht mit den C-Mocking-Bibliotheken oder Visual Studio beschäftigt, aber ich habe darüber in meinem eigenen Projekt nachgedacht. Die Feathers book schlägt die Präprozessor-Naht oder die Verbindungsnaht als ein Werkzeug vor, um damit umzugehen. Sie haben die Präprozessor-Naht bereits erwähnt, also konzentriere ich mich auf die Verbindungsnaht.

Der Link-Saum erfordert, dass sich die Mock-Funktion in einer Bibliothek befindet und dass sich die Mock-Funktion in einer Bibliothek befindet. Der Test kann mit der Mock-Funktionsbibliothek verknüpft werden, während die Zielanwendung mit der ursprünglichen Bibliothek verknüpft werden kann.

Natürlich müssen Sie, um MyFunc() zu mokieren, eine andere Bibliothek und eine separate Testanwendung erstellen, um eine Verknüpfung herzustellen (oder Bibliotheken in der Testanwendung dynamisch zu laden und zu entladen).

Das hört sich ziemlich mühsam an, weshalb ich das Hinzufügen von Tests in meiner eigenen Anwendung aufschiebe!

Hoffe, das hilft!