Ein hochauflösendes Foto belegt in einer iOS-Liste nur eine kleine Zelle, kann beim schnellen Scrollen aber wiederholt dekodiert werden. Lädt der Code zuerst das Original und skaliert anschließend das UIImage auf die Anzeigegröße, erscheint zwar eine Miniatur auf dem Bildschirm – die Dekodierung hat möglicherweise trotzdem das gesamte Bild verarbeitet. Für Regressionstests auf einem Cloud-Mac ist es verlässlicher, zunächst eine überprüfbare Vorgabe festzulegen: Die längste Kante der Miniatur darf eine bestimmte Pixelzahl nicht überschreiten. Allein zu beobachten, ob die Liste flüssig scrollt, reicht nicht aus.
Ausgabegrenze festlegen, statt von der Dateigröße auszugehen
Als Beispiel verwendet dieser Artikel eine maximale Kantenlänge von 320 Pixeln. Das ist kein allgemeingültiger Wert für jede Oberfläche. Im Projekt sollte sich die angeforderte Größe aus den Punktmaßen der Zelle, dem Skalierungsfaktor des Zielbildschirms und der Art des Bildausschnitts ergeben. Ein Bild für ein Display mit hohem Skalierungsfaktor darf beispielsweise nicht allein anhand der Punktmaße der Zelle in Pixeln bemessen werden. Sobald die Obergrenze feststeht, sollte der Einstiegspunkt zum Laden der Bilder sie als Parameter entgegennehmen. Die Konstante gehört nicht separat in jeden Listencontroller.
Legen Sie eine Test-JPEG namens landscape.jpg in den Ressourcen des Test-Targets ab. Sowohl Breite als auch Höhe des Originals müssen über der Testgrenze liegen. Andernfalls könnte der Test bestehen, selbst wenn die Implementierung unverändert das Original zurückgibt. Die Beispieldatei wird mit dem Repository eingecheckt und nicht erst zur Laufzeit aus dem Netz geladen. Halten Sie außerdem Bilder mit unterschiedlichen Seitenverhältnissen vor, damit nicht nur ein Layout geprüft wird. Kontrollieren Sie die Ressourcenkonfiguration des Test-Targets und stellen Sie sicher, dass sich die Datei zur Laufzeit tatsächlich aus dem Test-Bundle laden lässt.
Eine Prüfung der Ausgabemaße beantwortet die Frage „Wie groß ist das erzeugte Bild?“, nicht aber „Wie hoch ist der maximale Speicherverbrauch?“. Beides muss getrennt überprüft werden.
Miniatur am Einstiegspunkt der Dekodierung erzeugen
Erstellen Sie mit ImageIO eine Bildquelle aus der Datei-URL und geben Sie beim Erzeugen der Miniatur die maximale Kantenlänge in Pixeln an. Die Beispielfunktion liefert ein CGImage, dessen Breite und Höhe der Test direkt in Pixeln auslesen kann. Benötigt die Oberfläche ein UIImage, erfolgt die Umwandlung erst an der Aufrufstelle. Beim Erstellen der Bildquelle ist das sofortige Caching deaktiviert, damit nicht vorab das Original dekodiert wird. Beim Erzeugen der Miniatur wird dagegen die sofortige Dekodierung angefordert.
import Foundation
import ImageIO
func makeThumbnail(at url: URL, maxPixelSize: Int) -> CGImage? {
guard maxPixelSize > 0 else { return nil }
let sourceOptions: [CFString: Any] = [
kCGImageSourceShouldCache: false
]
guard let source = CGImageSourceCreateWithURL(
url as CFURL, sourceOptions as CFDictionary
) else {
return nil
}
let thumbnailOptions: [CFString: Any] = [
kCGImageSourceCreateThumbnailFromImageAlways: true,
kCGImageSourceCreateThumbnailWithTransform: true,
kCGImageSourceThumbnailMaxPixelSize: maxPixelSize,
kCGImageSourceShouldCacheImmediately: true
]
return CGImageSourceCreateThumbnailAtIndex(
source, 0, thumbnailOptions as CFDictionary
)
}
CreateThumbnailFromImageAlways sorgt hier dafür, dass der Test ausdrücklich den Pfad zur Miniaturerzeugung abdeckt. Mit CreateThumbnailWithTransform berücksichtigt die Ausgabe die Orientierungsinformationen des Bildes. Je nach Orientierung können Breite und Höhe vertauscht sein. Prüfen Sie deshalb die längere der beiden Kanten, statt „Breite muss größer als Höhe sein“ als allgemeine Regel für alle Bilder festzuschreiben. Bei ungültigen Eingaben oder wenn sich ein Bild nicht dekodieren lässt, gibt die Funktion nil zurück. Die Oberfläche braucht dafür einen sichtbaren Fehlerzustand; nicht jede Datei ist gültig.
Ausgabemaße mit festen Beispieldateien auf Regressionen prüfen
Legen Sie die produktive Funktion im App-Target ab und machen Sie sie für das Test-Target zugänglich. Der folgende Test setzt voraus, dass die Testressource zum Bundle hinzugefügt wurde. Liegt die Datei in einem Unterverzeichnis, passen Sie die Suche an die tatsächliche Ressourcenstruktur des Projekts an. Mit XCTUnwrap wird bei einer fehlenden Datei oder einem Dekodierungsfehler direkt die Fehlerstelle angezeigt, statt einen optionalen Wert unbemerkt zu übergehen.
import XCTest
import ImageIO
@testable import Sample
final class ThumbnailTests: XCTestCase {
func testThumbnailRespectsPixelLimit() throws {
let url = try XCTUnwrap(
Bundle(for: Self.self).url(
forResource: "landscape",
withExtension: "jpg"
)
)
let image = try XCTUnwrap(
makeThumbnail(at: url, maxPixelSize: 320)
)
XCTAssertGreaterThan(image.width, 0)
XCTAssertGreaterThan(image.height, 0)
XCTAssertLessThanOrEqual(
max(image.width, image.height), 320
)
}
func testRejectsInvalidLimit() throws {
let url = try XCTUnwrap(
Bundle(for: Self.self).url(
forResource: "landscape",
withExtension: "jpg"
)
)
XCTAssertNil(makeThumbnail(at: url, maxPixelSize: 0))
}
}
Ersetzen Sie Sample durch den tatsächlichen Modulnamen. Ergänzen Sie neben dem Größentest eine beschädigte Beispieldatei, um zu prüfen, ob der aufrufende Code mit nil umgehen kann. Eine Datei mit Orientierungsinformationen sollte außerdem bestätigen, dass das Bild wie erwartet ausgerichtet angezeigt wird. Herkunft und Nutzungsrechte der Testbilder müssen geklärt sein; Dateien aus einem privaten Fotoalbum gehören nicht ins Repository.
Auf dem Cloud-Mac ausführen und Fehlalarme untersuchen
Prüfen Sie zunächst mit xcrun simctl list devices available, welche Simulatoren installiert sind. Wählen Sie dann ein tatsächlich vorhandenes Gerät als destination für xcodebuild. Ersetzen Sie auch Projektname, scheme und Test-Target durch die Namen im Repository. Übernehmen Sie die Beispielparameter nicht in der Annahme, dass das angegebene Gerät verfügbar ist.
xcrun simctl list devices available
xcodebuild \
-project Sample.xcodeproj \
-scheme Sample \
-destination 'platform=iOS Simulator,name=iPhone 16' \
-only-testing:SampleTests/ThumbnailTests \
test
Meldet der Test, dass das Bild nicht gefunden wurde, prüfen Sie zuerst, ob es als Ressource zum Test-Target gehört, statt die Größengrenze anzuheben. Überschreitet die Ausgabe die Grenze, stellen Sie sicher, dass die Liste tatsächlich makeThumbnail aufruft und nicht wieder den alten Pfad nutzt, der zuerst ein UIImage aus dem Original erzeugt. In CI sollten Sie die Kommandoausgabe fehlgeschlagener Tests sichern und denselben Satz an Beispieldateien beibehalten. Werden Bilder ausgetauscht, ohne Pixelmaße und Orientierung zu prüfen, sind die Ergebnisse vor und nach der Änderung nicht mehr vergleichbar.
Größenprüfung und Speicherbeobachtung trennen
Der Größentest eignet sich für jeden Commit. Er bildet jedoch nicht ab, wie eine echte Liste gleichzeitig mehrere Bilder vorhält, Bilder zwischenspeichert und auf schnelles Scrollen reagiert. Scrollen Sie vor dem Mergen auf dem Zielgerät mit einer repräsentativen Bildauswahl wiederholt durch die Liste und nutzen Sie dabei den tatsächlichen Ladepfad. Beobachten Sie den maximalen Speicherverbrauch und ob Bilder wieder freigegeben werden. Halten Sie Gerät, Systemversion, Anzahl der Bilder und Arbeitsschritte fest, um die Werte vor und nach der Änderung zu vergleichen. Ein einzelner Messwert aus dem Simulator ist keine Aussage über alle Geräte.
Prüfen Sie zum Schluss drei Punkte: Wird die angeforderte Größe der Miniatur aus den Anzeigeanforderungen abgeleitet? Führen alle Einstiegspunkte zum Laden von Bildern durch dieselbe Downsampling-Funktion? Gibt es für ungültige Bilder einen sichtbaren Fehlerpfad? So sichern automatisierte Tests auf dem Cloud-Mac die Pixelgrenze ab, während Messungen auf dem Gerät die tatsächliche Speicherfrage beantworten. Beide Nachweise haben klar abgegrenzte Aussagebereiche.
Häufig gestellte Fragen
Reicht es, ein geladenes UIImage nachträglich zu verkleinern?
Nein. Das Original kann vorher bereits vollständig dekodiert worden sein. Die Pixelgrenze gehört in die Miniaturerzeugung mit ImageIO.
Beweist ein bestandener Größentest einen niedrigen Speicherbedarf?
Nein. Er prüft nur die Ausgabegröße. Den Spitzenverbrauch muss man mit repräsentativen Bildern und dem echten Scrollablauf auf einem Zielgerät messen.
Richten Sie einen Cloud-Mac für Ihre nächste Aufgabe ein
Wählen Sie einen Mac mini passend zum Arbeitsspeicherbedarf oder Build-Aufkommen. Drei Modellklassen sind tage-, wochen-, monats- oder quartalsweise buchbar. Die aktuelle Verfügbarkeit der Knoten sehen Sie in Echtzeit in der Konsole.