Les bibliothèques clientes Cloud pour Rust permettent de créer des bouchons d'implémentations clientes réelles afin que vous puissiez injecter un bouchon pour les tests. Vous pouvez utiliser des simulations pour écrire des tests unitaires contrôlés et fiables qui n'effectuent pas d'appels réseau et n'entraînent pas de frais facturés.
Dans ce document, vous allez découvrir les sujets suivants :
- Écrire des interfaces testables à l'aide du client
StorageCloud Storage. - Simuler des opérations de lecture
- Simuler des opérations d'écriture
- Pourquoi la conception du client
Storagediffère de celle des autres clients Google Cloud.
Dépendances
Ajoutez la bibliothèque cliente Cloud Storage à votre fichier Cargo.toml :
cargo add google-cloud-storage
Pour compiler et exécuter les tests unitaires de ce document, ajoutez les dépendances de développement suivantes :
cargo add --dev anyhow bytes mockall
cargo add --dev tokio --features macros,rt-multi-thread
Écrire des interfaces testables
Si vous n'effectuez pas de test unitaire de votre code de stockage, vous pouvez écrire des interfaces directement en termes de Storage, dont le paramètre de type de bouchon par défaut est l'implémentation de production du client :
Pour tester votre code avec un client fictif, écrivez vos interfaces en termes de paramètre de type générique T limité par gcs::stub::Storage + 'static :
Simuler des opérations de lecture
Pour simuler des requêtes read_object, procédez comme suit :
Écrivez une fonction d'application qui utilise un client
&Storage<T>générique. Par exemple, la fonction suivante télécharge un objet et compte le nombre de caractères de nouvelle ligne qu'il contient :Créez une fonction d'assistance qui construit un flux d'octets
ReadObjectResponsefactice en transmettant une charge utile àReadObjectResponse::from_source. Cette méthode accepte les mêmes types de charge utile queStorage::write_object:Définissez une structure fictive qui implémente
gcs::stub::Storage. Ce document utilise la caissemockall, mais vous pouvez utiliser un autre framework de simulation dans vos tests :Écrivez un test unitaire qui définit les attentes sur l'objet fictif, construit un client à l'aide de
Storage::from_stubet appellecount_newlines:
Simuler des opérations d'écriture
Pour simuler des requêtes write_object, procédez comme suit :
Écrivez une fonction d'application qui importe un objet à l'aide d'un client
&Storage<T>générique :À l'aide de la structure
MockStoragedéfinie dans la section précédente, écrivez un test unitaire qui configure les attentes sur le simulacre, construit un client à l'aide deStorage::from_stubet appelleupload:
Détails de l'attente
Étant donné que la fonction upload appelle send_unbuffered, définissez l'attente sur la méthode expect_write_object_unbuffered correspondante :
La macro mockall::mock! traite chaque instanciation générique comme une méthode distincte. Spécifiez le type de charge utile concret, Payload<BytesSource>, dans la signature de la fermeture afin que le compilateur puisse correspondre à l'attente de la méthode :
Justification de la conception
Les sections suivantes expliquent pourquoi le client Storage utilise un paramètre de type générique pour son bouchon plutôt qu'une distribution dynamique.
Autres clients
La plupart des clients, tels que StorageControl, contiennent en interne une implémentation compatible avec dyn de leur trait de bouchon, avec comptage de références (Arc). Ils utilisent la distribution dynamique pour transférer les requêtes du client vers le bouchon sous-jacent, qui peut être l'implémentation de production ou un bouchon.
Comme ces clients utilisent la distribution dynamique, le compilateur n'a pas besoin de résoudre le type de bouchon concret au temps de compilation, et les structures client n'ont pas besoin d'un paramètre de type générique pour leur bouchon.
Le client Storage
En Rust, un trait compatible avec dyn ne peut pas avoir de méthodes génériques ni renvoyer de valeurs impl Future non empaquetées :
- Les méthodes
write_object_bufferedetwrite_object_unbufferedsont génériques par rapport au type de source de la charge utile. - Les méthodes de trait
gcs::stub::Storagerenvoient directementimpl Futureau lieu de boxer le futur renvoyé.
L'utilisation de la distribution dynamique pour le client Storage nécessiterait de boxer les charges utiles et les futures à chaque appel, ce qui ajouterait des allocations de segments de mémoire et une surcharge de distribution dynamique sur le chemin des données. Pour éviter cette surcharge, le client Storage utilise un paramètre de type pour une implémentation concrète du trait gcs::stub::Storage.
Code d'application complet et suite de tests
Le code suivant combine les fonctions d'application et les tests unitaires dans un exemple complet :
Exécutez cargo test pour exécuter test_count_lines et test_upload :
cargo test