let’s dev

Blog

Top!

let’s dev | Scroll to top
let’s dev | Scroll to next content item

Hello

let’s dev | Scroll to previous content item
let’s dev Blog | Automatisiertes Testing von C++ Code mit Google Test und Google Mock – Teil 2
by Arne
13. Juni 2019

Automatisiertes Testing von C++ Code mit Google Test und Google Mock – Teil 2

Das Testen von Applikationen ist auch im mobilen Umfeld ein wichtiger Bestandteil des Entwicklungsprozesses. Bei Bibliotheken, die in C++ geschrieben sind, wird dies durch Google Test und Google Mock ermöglicht. Nachdem wir bereits Google Test vorgestellt haben, betrachten wir im zweiten Teil das Mock-Framework Google Mock genauer.

Google Mock

Wie der Name bereits vermuten lässt, ist Google Mock ein Mock-Framework. Mit Hilfe des Frameworks kann leicht überprüft werden, ob Funktionen wie erwartet aufgerufen und definiert werden, was eine noch zu implementierende Funktion zurückgeben soll. Integration und Build funktionieren analog zu dem bereits vorgestellten Framework Google Test.

Hierbei sind verschiedene Arten von Tests zu unterscheiden. Als Beispiele seien Unit-Tests zum Testen einer Komponente oder Integration-Tests, die das Zusammenspiel mehrerer Komponenten prüfen, genannt. Für die meisten Programmiersprachen gibt es zudem Frameworks, die das Erstellen und Ausführen bedeutend erleichtern, so auch für C++.

Allgemeines

Die Verwendung von Google Mock ist neben Google Test auch mit anderen Test-Frameworks möglich, aufwendiger ist dann jedoch die bereits erwähnte Integration. Daher beziehen sich die nachfolgenden Ausführungen auf die gemeinsame Verwendung mit Google Test.

Zunächst muss hierfür der Header ergänzt werden. Zudem ist es sinnvoll, den Namespace „testing“ sichtbar zu machen:

# include <gtest/gtest.h>
# include <gmock/gmock.h>
using namespace testing ;

Um zu verstehen, wie Google Mock funktioniert, ist es notwendig sich zunächst mit der Begrifflichkeit Mock auseinanderzusetzen. Ein Mock Objekt implementiert dasselbe Interface wie das reale Objekt. Der wesentliche Unterschied ist jedoch, dass ein Mock-Objekt zur Laufzeit Informationen darüber erhält, welche Aufrufe auf es erwartet werden und wie es reagieren soll.

Erstellung eines Mock Objektes

Zum Mocken von Funktionen werden die Makros MOCK_METHOD benötigt. Wie diese umzusetzen sind, wird nachfolgend an einem Beispiel erklärt.

Als Beispiel dient eine Applikation, die eine Liste von Usern anzeigen soll. Diese ist nach dem verbreiteten Model View Controller (MVC) Konzept erstellt worden und enthält einen UserListController. Dieser Controller ist die Funktion, die getestet werden soll. Die UI, die auf diesen Controller gesetzt wird, soll auf gewisse Situationen reagieren. Sobald das Laden der User abgeschlossen ist, muss die UI aktualisiert werden. Falls ein User gelöscht wurde, muss dieser aus der Liste entfernt werden. Über den Contoller, kann hierfür ein Listener registriert werden. Der Controller benötigt außerdem Informationen wie den aktuellen Account, für den er die Liste der User laden soll. Hierfür wird dem Controller ein DataProvider injiziert.

Die beiden Interfaces sollen wie folgt definiert sein:

UserListControllerListener
class UserListControllerListener {
public :
virtual void onRefreshData () = 0;
virtual void onRemoveItem (int position ) = 0;
};
UserListControllerDataProvider
class UserListControllerDataProvider {
private :
virtual long getAccountId () = 0;
};

Nun werden die beiden Mocks definiert, die es ermöglichen sollen, das Verhalten der UserListController Schnittstellen zu testen. Dazu wird eine neue Klasse angelegt, welche die Mock-Implementierung beinhaltet.

UserListControllerListenerMock
class UserListControllerListenerMock : public UserListControllerListener
{
public :
MOCK_METHOD0 ( onRefreshData , void ());
MOCK_METHOD1 ( onRemoveItem , void (int));
};
UserListControllerDataProviderMock
class UserListControllerDataProviderMock : public
UserListControllerDataProvider {
public :
MOCK_METHOD0 ( getAccountId , long ());
};

Wie zu erkennen ist, wird der Mock im Header implementiert, weiterer Code in einer Cpp ist hierfür nicht notwendig. Andere Dinge, die weniger ersichtlich sind, müssen jedoch auch berücksichtigt werden. Die Mock-Implementierung erbt von der zu mockenden Klasse UserListControllerDataProviderMock : public UserListControllerDataProvider. Die Vererbung muss public sein, ansonsten kann es zu Kompilierfehlern kommen. Das Gleiche gilt auch auf die Sichtbarkeit innerhalb der Klasse. Im UserListControllerDataProvider ist die Funktion getAccountId als private markiert, im zugehörigen Mock jedoch als public. Dieser Umstand ist in C++ jedoch unproblematisch, da beim Überschreiben einer Funktion deren Sichtbarkeit verändert werden kann. Es ist also möglich, Funktionen im Code mit beliebiger Sichtbarkeit zu definieren. Es muss lediglich bei der Implementierung des Mocks darauf geachtet werden, diese dort als public zu markieren.

Die Makros selbst haben eine Zahl am Ende z.B. MOCK_METHOD0. Die Zahl entspricht der Anzahl, der Parameter, die die zu mockende Funktion besitzt. Auch hier erhält man eine kryptische Fehlermeldung beim Kompilieren, sollten die Anzahlen nicht übereinstimmen.

Es sei außerdem erwähnt, dass es für die Implementierung des Mock nicht von Bedeutung ist, ob es sich um rein virtuelle Funktionen handelt oder nicht. Allerdings ändert sich für nicht virtuelle Funktionen die Implementierung geringfügig.

Wer sich Zeit sparen und die Mock-Klassen nicht von Hand schreiben möchte, kann ein von Google bereitgestelltes Python Skript verwenden, das von einem Header die Mock-Implementierung generieren kann. Gerade bei kleineren Interfaces sollte jedoch der zeitliche Aufwand abgewogen werden.

Expectations

Für Tests, die Mocks verwenden, muss festgelegt werden, was von den jeweiligen Mocks erwartet wird. Wesentlich hierbei ist jedoch, die richtigen Erwartungen zu haben. Sind diese zu strikt, schlagen die Tests möglicherweise auch bei Änderungen fehl, die nichts mit dem jeweiligen Test zu tun haben. Werden jedoch zu lockere Erwartungen gestellt, können mögliche Fehler im Code eventuell übersehen werden.

Für die Erstellung von Expectations stehen verschiedene Konfigurationsmöglichkeiten zur Verfügung, die an einem Mock vorgenommen werden können:

Die Begriffe Matchers, Cardinalities und Actions werden von Google vorgegeben.

Expectations mit Cardinalities

Betrachtet werden soll eine einfache Expectation anhand des oben beschriebenen Beispiels. Getestet wird, ob das onRefreshData aufgerufen wird. Dabei wird davon ausgegangen, dass die Instanz des UserListControllerListenerMock bereits erzeugt und auf dem UserListController als Listener registriert wurde. Die Instanz des Mock Objektes ist der mockListener. Die definierte Expectation ist wie folgt definiert:

EXPECT_CALL ( mockListener , onRefreshData ()). Times (1);
mUserListControler -> loadData ();

Dem Mock-Listener wird mitgegeben, dass die Funktion onRefreshData genau einmal aufgerufen werden soll. Wird sie hingegen zweimal oder überhaupt nicht aufgerufen, schlägt der Test fehl. Zunächst muss das Mock konfiguriert werden, bevor der Aufruf ausgeführt wird, welcher das Mock-Objekt verwendet.

Die Kardinalität kann als direkte Zahl angegeben werden. In diesem Fall wird erwartet, dass die Funktion genau einmal aufgerufen wird. Auch Null ist eine korrekte Angabe. Damit wird überprüft, dass die Funktion nicht aufgerufen wird. Es ist außerdem möglich, eine Mindestanzahl anzugeben:

EXPECT_CALL ( mockListener , onRefreshData ()). Times ( AtLeast (1));

Im obigen Beispiel muss die Funktion mindestens einmal aufgerufen werden, damit der Test erfüllt ist. Er ist jedoch auch erfolgreich, wenn sie mehr als einmal aufgerufen wird.

Expectations mit Matchers

Im nachfolgenden Beispiel werden die Argumente des Aufrufs überprüft:

EXPECT_CALL ( mockListener , onRemoveItem (Eq (5)). Times (1) ;

Es soll getestet werden, ob die Funktion onRemoveItem mit dem Argument 5 aufgerufen wird. Eq ist ein Matcher, der bereits im Abschnitt Assertion mit Matchern (Teil 1 des Beitrags) auftaucht. Es ist also möglich eine ganze Reihe verschiedener Überprüfungen vorzunehmen. Es stehen Matcher für viele mathematische Vergleiche (z.B. Ge() ist ≥) zur Verfügung. Die Existenz von Objekten kann mit den Matchern IsNull() und IsNotNull() geprüft werden. Der Parameter „_“ steht für die Wildcard Any. Wird er anstelle von Eq(5) übergeben, ist der Aufruf mit jedem Parameter gültig.

Expectations mit Actions

Mit Actions können einem Mock bestimmte Verhalten übergeben werden. Ein einfaches Beispiel hierfür ist die Definition eines Rückgabewertes.

EXPECT_CALL ( mockDataProvider , getAccountId (). WillOnce ( Return (17) );

Es wird während des Aufrufs der Funktion der Wert 17 zurückgegeben. Alle Funktionen sind in einem EXPECT_CALL verwendbar. Es ist auch keine explizite Kardinalität vorgegeben. Google Mock kann anhand der Aufrufe von WillOnce die Kardinalität errechnen.

Mehrere Expectations in einem Test

Es kann durchaus vorkommen, dass mehr als eine Expectation in einem Integration-Test enthalten ist. Werden mehrere Expectations innerhalb einer Funktion angewandt, ist zudem die Reihenfolge der Verarbeitung bzw. Auswertung relevant. Expectations werden in der umgekehrten Reihenfolge ihrer Definition ausgewertet. Für die Action und Kardinalität wird immer die erste Expectation herangezogen, deren Matcher passt.

Beispiel für die Auswertung von Expectations

Mehrere Expectations
EXPECT_CALL ( mockListener , onRemoveItem (_)). Times ( AtLeast (1));
EXPECT_CALL ( mockListener , onRemoveItem (Eq (42) )). Times (3);

Es sind mehrere Szenarien denkbar, die in verschiedenen Ergebnissen resultieren. Wird die Funktion onRemoveItem viermal mit dem Wert 42 aufgerufen, so schlägt dieser Test fehl. Bei allen Aufrufen mit dem Wert 42 greift die untere Expectation. Im Test wird über Times (3) vorgegeben, dass der Aufruf mit 42 dreimal erwartet wird. Wenn die Funktion jedoch dreimal mit dem Wert 42 aufgerufen wird, ohne dass ein weiterer Aufruf erfolgt, so schlägt der Test fehl. Denn in der ersten Zeile ist definiert, dass die Funktion mindestens einmal mit einem beliebigen anderen Wert aufgerufen werden soll. So ist der Test erfolgreich, wenn zunächst dreimal der Wert 42 aufgerufen wird und anschließend ein weiterer mit 75 erfolgt.

Zusammenfassung

Die Ausführungen zeigen auf, dass Google Test und Google Mock mächtige Werkzeuge für das Schreiben von automatisierten Tests in C++ sind. Die Bibliotheken können unter der Verwendung von CMake einfach in bestehende Projekte integriert werden.

Google Test bietet eine Vielzahl einfacher Möglichkeiten, Assertions zu schreiben. Tests können durch Log-Nachrichten und Scoped Traces mit weiteren Informationen ausgestattet werden. Zudem bietet das Framework die Möglichkeit, komplexere Themen wie Exceptions und Aborts zu prüfen.

Die größte Limitierung der Frameworks stellt hingegen der fehlende Support für Tests von asynchronen Funktionalitäten dar. Auch dass Google Test unter manchen Plattformen nicht als Threadsave gilt, könnte Anwender dieser Plattformen vor Herausforderungen stellen.

Weitere Artikel aus unserem Blog

let’s dev Blog | World Usability Day 2019 in Karlsruhe – let’s dev unterstützt als Hauptsponsor

Corporate

World Usability Day 2019 in Karlsruhe – let’s dev unterstützt als Hauptsponsor

by Aileen

2019-11-11

Weiterlesen
let’s dev Blog | Ausgeschlachtet - Tag der offenen Türen auf dem Alten Schlachthof Karlsruhe 2019

Corporate

Ausgeschlachtet - Tag der offenen Türen auf dem Alten Schlachthof Karlsruhe 2019

by Julian

2019-09-26

Weiterlesen
let’s dev Blog | Mensch und Computer 2019 - Konferenz zu User Experience und Usability in Hamburg

Corporate

Mensch und Computer 2019 - Konferenz zu User Experience und Usability in Hamburg

by Elena

2019-09-17

Weiterlesen
let’s dev Blog | Business und Enterprise App Verteilung unter iOS

Technical

Business und Enterprise App Verteilung unter iOS

by Aileen

2019-08-05

Weiterlesen
let’s dev Blog | Digital Transformation - Chances and Challenges in the Automotive Industry, Agriculture and New Technologies

Corporate

Digital Transformation - Chances and Challenges in the Automotive Industry, Agriculture and New Technologies

by Karl

2019-07-17

Weiterlesen
let’s dev Blog | let’s dev unterstützt Läuferinnen und Läufer bei der 7. KIT Meisterschaft

Corporate

let’s dev unterstützt Läuferinnen und Läufer bei der 7. KIT Meisterschaft

by Karl

2019-07-05

Weiterlesen
let’s dev Blog | Apple WWDC 2019: Das sind die Highlights der Keynote

Technical

Apple WWDC 2019: Das sind die Highlights der Keynote

by Nicolas

2019-06-05

Weiterlesen
let’s dev Blog | App Builders 2019

Technical

App Builders 2019

by Nicolas

2019-05-23

Weiterlesen
let’s dev Blog | Offizielle Eröffnung des Festigungs- und Expansionszentrums (FUX)

Corporate

Offizielle Eröffnung des Festigungs- und Expansionszentrums (FUX)

by Helena

2019-04-15

Weiterlesen
let’s dev Blog | Delegation aus Nottingham zu Besuch auf dem Alten Schlachthof in Karlsruhe

Corporate

Delegation aus Nottingham zu Besuch auf dem Alten Schlachthof in Karlsruhe

by Helena

2019-04-14

Weiterlesen
let’s dev Blog | Es ist soweit: Wir ziehen um!

Corporate

Es ist soweit: Wir ziehen um!

by Helena

2019-03-26

Weiterlesen
let’s dev Blog | Automatisiertes Testing von C++ Code mit Frameworks – Teil 1

Technical

Automatisiertes Testing von C++ Code mit Frameworks – Teil 1

by Arne

2019-02-20

Weiterlesen
let’s dev Blog | Die App im Google Play Store

Technical

Die App im Google Play Store

by Elisa

2019-01-24

Weiterlesen
let’s dev Blog | „UX Day“ 2018

Corporate

„UX Day“ 2018

by Aileen

2018-12-17

Weiterlesen
let’s dev Blog | let’s dev unterstützt SG Siemens Volleyballer aus Karlsruhe

Corporate

let’s dev unterstützt SG Siemens Volleyballer aus Karlsruhe

by Helena

2018-12-04

Weiterlesen
let’s dev Blog | KMU gestalten die Digitalisierung – Mittelstandskonferenz 2018

Corporate

KMU gestalten die Digitalisierung – Mittelstandskonferenz 2018

by Helena

2018-11-12

Weiterlesen
let’s dev Blog | Apple Wallet

Technical

Apple Wallet

by Maik

2018-10-26

Weiterlesen
let’s dev Blog | „Mensch und Computer“ 2018

Corporate

„Mensch und Computer“ 2018

by Judith

2018-09-24

Weiterlesen
let’s dev Blog | State Design Pattern in Android

Technical

State Design Pattern in Android

by Thomas

2018-09-17

Weiterlesen
let’s dev Blog | let’s dev ist autorisiertes Beraterunternehmen im Förderprogramm „go-digital“

Corporate

let’s dev ist autorisiertes Beraterunternehmen im Förderprogramm „go-digital“

by Helena

2018-09-01

Weiterlesen
let’s dev Blog | App Design & Development Conference 2018

Corporate

App Design & Development Conference 2018

by Helena

2018-08-14

Weiterlesen
let’s dev Blog | iOS 12: Die Top-Neuerungen im Überblick

Technical

iOS 12: Die Top-Neuerungen im Überblick

by Nicolas

2018-07-17

Weiterlesen
let’s dev Blog | let’s dev auf der CEBIT

Corporate

let’s dev auf der CEBIT

by Karl

2018-06-11

Weiterlesen
let’s dev Blog | Einführung in User Interface (UI) Tests mit Espresso

Technical

Einführung in User Interface (UI) Tests mit Espresso

by Raphael

2018-06-07

Weiterlesen
let’s dev Blog | Die App im Apple App Store: welche Informationen werden benötigt?

Technical

Die App im Apple App Store: welche Informationen werden benötigt?

by Aileen

2018-04-27

Weiterlesen
let’s dev Blog | Smart Pointer in C++

Technical

Smart Pointer in C++

by Matthias

2018-04-01

Weiterlesen
let’s dev Blog | User Interface Design für das iPhone X: alle Neuerungen auf einen Blick

Technical

User Interface Design für das iPhone X: alle Neuerungen auf einen Blick

by Helena

2018-02-07

Weiterlesen
let’s dev Blog | WebVR – Virtual Reality Experience im Browser mit dem A-Frame Framework

Technical

WebVR – Virtual Reality Experience im Browser mit dem A-Frame Framework

by Judith

2018-01-10

Weiterlesen
let’s dev Blog | Open Data Hackathon der Deutschen Bahn

Corporate

Open Data Hackathon der Deutschen Bahn

by Karl

2015-03-31

Weiterlesen
let’s dev Blog | Blur-Effekte unter iOS 7

Technical

Blur-Effekte unter iOS 7

by Katja

2014-04-24

Weiterlesen
let’s dev Blog | Beyond App Store – Distribution von iOS-Anwendungen

Technical

Beyond App Store – Distribution von iOS-Anwendungen

by Karl

2012-08-27

Weiterlesen
let’s dev Blog | Frontend-Architektur – Model View Presenter und Message Bus

Technical

Frontend-Architektur – Model View Presenter und Message Bus

by Karl

2011-03-08

Weiterlesen