Der sanfte Weg von Objective-C zu Swift

Seite 3: Unit-Tests und Mocks

Inhaltsverzeichnis

Da Unit-Tests gerade bei der Migration einen großen Stellenwert einnehmen, lohnt sich der Blick auf die diesbezüglichen Eigenheiten bei Swift. Seit Xcode 6 Beta 4 lässt sich die Sichtbarkeit von Methoden und Variablen in Swift durch die Schlüsselwörter private, internal und public steuern. Das hat einige Auswirkungen auf die Unit-Tests: Da sie in einem separaten Modul in Xcode liegen, sind Methoden und Variablen, die als internal oder private markiert sind, zunächst für den Test nicht sichtbar und deshalb auch nicht direkt testbar.

Man hat nun zwei Möglichkeiten:

  1. Alle Methoden und Variablen, die getestet werden sollen, lassen sich durch das Schlüsselwort public für den Test sichtbar machen.
  2. Die Klassen, die getestet werden sollen, kann man dem Testmodul hinzufügen. Das geschieht in Xcode im File Inspector der Swift-Klasse, indem man für das Testmodul das Häkchen bei Target Membership setzt. Auf die Weise ist alles sichtbar, was mindestens internal ist. Letzteres ist die Standardeinstellung, wenn nicht explizit eine andere Sichtbarkeit angegeben ist.

Durch die Häkchen unter "Target Membership" ist alles für den Test Nötige sichtbar (Abb. 2).

Die augenscheinlich bessere Möglichkeit ist die Variante zwei, da Entwickler bei ihr den Code nicht für die Tests manipulieren müssen. Grundsätzlich bietet es sich an, jede Variable und Methode als private zu markieren, die nicht außerhalb der Klasse gebraucht wird.

Jeder, der sich intensiver mit Unit-Tests beschäftigt, ist wahrscheinlich schon einmal mit Mocks in Berührung gekommen. Dabei handelt es sich um Instanzen von Klassen, deren Verhalten zur Laufzeit des Tests definiert wird. So ist es beispielsweise einfach, eine Datenbankverbindung in Form eines NSManagedObjectContext zu mocken, damit während des Tests nicht wirklich eine Datenbank zur Verfügung stehen muss. In Objective-C verwendet man zu dem Zweck zum Beispiel das Mocking-Framework OCMock.

Leider ist das mit Swift nicht ganz so einfach, da kein Zugriff auf die dafür benötigten Features der Runtime vorhanden ist. Allerdings lässt sich das Problem relativ elegant umschiffen, da die Sprache dafür andere Features enthält. So lässt sich ein Mock einfach durch eine innere Klasse erzeugen. Im Falle von NSManagedObjectContext könnte das so aussehen:

func testDatabaseConnection() {
class MockNSManagedObjectContext : NSManagedObjectContext {

override func executeFetchRequest(request: NSFetchRequest,
error: NSErrorPointer) -> [AnyObject]? {
return ["row"]
}
}
let mock = MockNSManagedObjectContext()
let testClass = FirstSwiftClass(context: mock)
let result = testClass.getObjectsFromDatabase();
XCTAssertEqual(["row"], result)
}