Die Cloud-Clientbibliotheken für Rust bieten eine Möglichkeit, echte Clientimplementierungen zu stubben, sodass Sie ein Mock-Objekt für Tests einfügen können. Mit Mocks können Sie kontrollierte, zuverlässige Unittests schreiben, die keine Netzwerkaufrufe ausführen und keine Kosten verursachen.
In diesem Dokument werden die folgenden Themen behandelt:
- Anleitung zum Schreiben testbarer Schnittstellen mit dem Cloud Storage-
Storage-Client. - Lesevorgänge simulieren
- Scheinschreibvorgänge erstellen
- Warum sich das Design des
Storage-Clients von anderen Google Cloud-Clients unterscheidet.
Abhängigkeiten
Fügen Sie die Cloud Storage-Clientbibliothek der Datei Cargo.toml hinzu:
cargo add google-cloud-storage
Fügen Sie die folgenden Entwicklungsabhängigkeiten hinzu, um die Unittests in diesem Dokument zu kompilieren und auszuführen:
cargo add --dev anyhow bytes mockall
cargo add --dev tokio --features macros,rt-multi-thread
Testbare Schnittstellen schreiben
Wenn Sie Ihren Speichercode nicht mit Unittests testen, können Sie Schnittstellen direkt in Form von Storage schreiben, dessen Standard-Stub-Typparameter die Produktionsimplementierung des Clients ist:
Wenn Sie Ihren Code mit einem simulierten Client testen möchten, schreiben Sie Ihre Schnittstellen in Form eines generischen Typparameters T, der durch gcs::stub::Storage + 'static eingeschränkt ist:
Mock-Lesevorgänge
Führen Sie die folgenden Schritte aus, um read_object-Anfragen zu simulieren:
Schreiben Sie eine Anwendungsfunktion, die einen generischen
&Storage<T>-Client verwendet. Mit der folgenden Funktion wird beispielsweise ein Objekt heruntergeladen und die Anzahl der darin enthaltenen Zeilenumbruchzeichen gezählt:Erstellen Sie eine Hilfsfunktion, die einen gefälschten
ReadObjectResponse-Bytestream erstellt, indem eine Nutzlast anReadObjectResponse::from_sourceübergeben wird. Diese Methode akzeptiert dieselben Nutzlasttypen wieStorage::write_object:Definieren Sie eine Mock-Struktur, die
gcs::stub::Storageimplementiert. In diesem Dokument wird diemockall-Crate verwendet. Sie können jedoch auch ein anderes Mocking-Framework in Ihren Tests verwenden:Schreiben Sie einen Unittest, der Erwartungen an das Mock-Objekt festlegt, einen Client mit
Storage::from_stuberstellt undcount_newlinesaufruft:
Schreibvorgänge simulieren
Führen Sie die folgenden Schritte aus, um write_object-Anfragen zu simulieren:
Schreiben Sie eine Anwendungsfunktion, die ein Objekt mit einem generischen
&Storage<T>-Client hochlädt:Schreiben Sie mit der im vorherigen Abschnitt definierten Struktur
MockStorageeinen Unittest, der Erwartungen an das Mock-Objekt konfiguriert, einen Client mitStorage::from_stuberstellt unduploadaufruft:
Details zur Erwartung
Da die Funktion upload die Funktion send_unbuffered aufruft, legen Sie die Erwartung für die entsprechende Methode expect_write_object_unbuffered fest:
Das Makro mockall::mock! behandelt jede generische Instanziierung als separate Methode. Geben Sie den konkreten Nutzlasttyp Payload<BytesSource> in der Closure-Signatur an, damit der Compiler die Methodenerwartung erfüllen kann:
Designbegründung
In den folgenden Abschnitten wird erläutert, warum der Storage-Client einen generischen Typparameter für seinen Stub anstelle von dynamischem Dispatch verwendet.
Andere Clients
Die meisten Clients, z. B. StorageControl, enthalten intern eine referenzgezählte (Arc), dyn-kompatible Implementierung ihres Stub-Traits. Sie verwenden dynamisches Dispatching, um Anfragen vom Client an den zugrunde liegenden Stub weiterzuleiten, der entweder die Produktionsimplementierung oder ein Mock sein kann.
Da diese Clients dynamisches Dispatching verwenden, muss der Compiler den konkreten Stub-Typ nicht zur Kompilierzeit auflösen und die Client-Strukturen benötigen keinen generischen Typparameter für ihren Stub.
Der Storage-Client
In Rust kann ein dyn-kompatibles Trait keine generischen Methoden haben oder unboxed impl Future-Werte zurückgeben:
- Die Methoden
write_object_bufferedundwrite_object_unbufferedsind generisch für den Nutzlastquelltyp. - Die
gcs::stub::Storage-Trait-Methoden gebenimpl Futuredirekt zurück, anstatt das zurückgegebene Future zu verpacken.
Wenn Sie die dynamische Weiterleitung für den Storage-Client verwenden, müssen Sie bei jedem Aufruf Payloads und Futures verpacken, was zu Heap-Zuweisungen und dynamischem Weiterleitungs-Overhead im Datenpfad führt. Um diesen Mehraufwand zu vermeiden, verwendet der Storage-Client stattdessen einen Typparameter für eine konkrete Implementierung des gcs::stub::Storage-Traits.
Vollständiger Anwendungscode und Test-Suite
Der folgende Code kombiniert die Anwendungsfunktionen und Unit-Tests zu einem vollständigen Beispiel:
Führen Sie cargo test aus, um test_count_lines und test_upload auszuführen:
cargo test