Générez automatiquement des maquettes mockall à l'aide du AWS SDK pour Rust - Kit AWS SDK pour Rust

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

Générez automatiquement des maquettes mockall à l'aide du AWS SDK pour Rust

Kit AWS SDK pour Rust Il fournit plusieurs approches pour tester votre code qui interagit avec Services AWS. Vous pouvez générer automatiquement la majorité des implémentations fictives dont vos tests ont besoin en utilisant la solution populaire « automock from the mockall crate ».

Cet exemple teste une méthode personnalisée appeléedetermine_prefix_file_size(). Cette méthode appelle une méthode list_objects() wrapper personnalisée qui appelle Amazon S3. En se moquantlist_objects(), la determine_prefix_file_size() méthode peut être testée sans contacter Amazon S3.

  1. Dans une invite de commande pour le répertoire de votre projet, ajoutez le mockall crate en tant que dépendance :

    $ cargo add --dev mockall

    L'utilisation de --dev cette option ajoute la caisse à la [dev-dependencies] section de votre Cargo.toml fichier. En tant que dépendance de développement, elle n'est pas compilée et incluse dans votre binaire final utilisé pour le code de production.

    Cet exemple de code utilise également Amazon Simple Storage Service comme exemple Service AWS.

    $ cargo add aws-sdk-s3

    Cela ajoute la caisse à la [dependencies] section de votre Cargo.toml fichier.

  2. Incluez le automock module de la mockall caisse.

    Incluez également toute autre bibliothèque liée à celle Service AWS que vous testez, dans ce cas, Amazon S3.

    use aws_sdk_s3 as s3; #[allow(unused_imports)] use mockall::automock; use s3::operation::list_objects_v2::{ListObjectsV2Error, ListObjectsV2Output};
  3. Ajoutez ensuite du code qui détermine laquelle des deux implémentations de la structure d'enveloppe Amazon S3 de l'application doit être utilisée.

    • Le vrai écrit pour accéder à Amazon S3 via le réseau.

    • L'implémentation fictive générée parmockall.

    Dans cet exemple, le nom est attribué à celui qui est sélectionnéS3. La sélection est conditionnelle en fonction de l'testattribut :

    #[cfg(test)] pub use MockS3Impl as S3; #[cfg(not(test))] pub use S3Impl as S3;
  4. La S3Impl structure est l'implémentation de la structure d'emballage Amazon S3 qui envoie réellement des demandes à. AWS

    • Lorsque le test est activé, ce code n'est pas utilisé car la demande est envoyée au simulateur et non AWS. L'dead_codeattribut indique au linter de ne pas signaler de problème si le S3Impl type n'est pas utilisé.

    • Le conditionnel #[cfg_attr(test, automock)] indique que lorsque le test est activé, l'automockattribut doit être défini. Cela indique mockall de générer une maquette S3Impl qui sera nomméeMockS3Impl.

    • Dans cet exemple, la list_objects() méthode est l'appel que vous souhaitez simuler. automockcréera automatiquement une expect_list_objects() méthode pour vous.

    #[allow(dead_code)] pub struct S3Impl { inner: s3::Client, } #[cfg_attr(test, automock)] impl S3Impl { #[allow(dead_code)] pub fn new(inner: s3::Client) -> Self { Self { inner } } #[allow(dead_code)] pub async fn list_objects( &self, bucket: &str, prefix: &str, continuation_token: Option<String>, ) -> Result<ListObjectsV2Output, s3::error::SdkError<ListObjectsV2Error>> { self.inner .list_objects_v2() .bucket(bucket) .prefix(prefix) .set_continuation_token(continuation_token) .send() .await } }
  5. Créez les fonctions de test dans un module nommétest.

    • Le conditionnel #[cfg(test)] indique qui mockall doit créer le module de test si l'testattribut esttrue.

    #[cfg(test)] mod test { use super::*; use mockall::predicate::eq; #[tokio::test] async fn test_single_page() { let mut mock = MockS3Impl::default(); mock.expect_list_objects() .with(eq("test-bucket"), eq("test-prefix"), eq(None)) .return_once(|_, _, _| { Ok(ListObjectsV2Output::builder() .set_contents(Some(vec![ // Mock content for ListObjectsV2 response s3::types::Object::builder().size(5).build(), s3::types::Object::builder().size(2).build(), ])) .build()) }); // Run the code we want to test with it let size = determine_prefix_file_size(mock, "test-bucket", "test-prefix") .await .unwrap(); // Verify we got the correct total size back assert_eq!(7, size); } #[tokio::test] async fn test_multiple_pages() { // Create the Mock instance with two pages of objects now let mut mock = MockS3Impl::default(); mock.expect_list_objects() .with(eq("test-bucket"), eq("test-prefix"), eq(None)) .return_once(|_, _, _| { Ok(ListObjectsV2Output::builder() .set_contents(Some(vec![ // Mock content for ListObjectsV2 response s3::types::Object::builder().size(5).build(), s3::types::Object::builder().size(2).build(), ])) .set_next_continuation_token(Some("next".to_string())) .build()) }); mock.expect_list_objects() .with( eq("test-bucket"), eq("test-prefix"), eq(Some("next".to_string())), ) .return_once(|_, _, _| { Ok(ListObjectsV2Output::builder() .set_contents(Some(vec![ // Mock content for ListObjectsV2 response s3::types::Object::builder().size(3).build(), s3::types::Object::builder().size(9).build(), ])) .build()) }); // Run the code we want to test with it let size = determine_prefix_file_size(mock, "test-bucket", "test-prefix") .await .unwrap(); assert_eq!(19, size); } }
    • Chaque test permet let mut mock = MockS3Impl::default(); de créer une mock instance deMockS3Impl.

    • Il utilise la expect_list_objects() méthode de la maquette (qui a été créée automatiquement parautomock) pour définir le résultat attendu lorsque la list_objects() méthode est utilisée ailleurs dans le code.

    • Une fois les attentes établies, il les utilise pour tester la fonction en appelantdetermine_prefix_file_size(). La valeur renvoyée est vérifiée pour confirmer qu'elle est correcte, à l'aide d'une assertion.

  6. La determine_prefix_file_size() fonction utilise le wrapper Amazon S3 pour obtenir la taille du fichier de préfixe :

    #[allow(dead_code)] pub async fn determine_prefix_file_size( // Now we take a reference to our trait object instead of the S3 client // s3_list: ListObjectsService, s3_list: S3, bucket: &str, prefix: &str, ) -> Result<usize, s3::Error> { let mut next_token: Option<String> = None; let mut total_size_bytes = 0; loop { let result = s3_list .list_objects(bucket, prefix, next_token.take()) .await?; // Add up the file sizes we got back for object in result.contents() { total_size_bytes += object.size().unwrap_or(0) as usize; } // Handle pagination, and break the loop if there are no more pages next_token = result.next_continuation_token.clone(); if next_token.is_none() { break; } } Ok(total_size_bytes) }

Le type S3 est utilisé pour appeler le SDK encapsulé pour les fonctions Rust afin de prendre en charge les deux S3Impl et MockS3Impl lors de l'envoi de requêtes HTTP. La maquette générée automatiquement par mockall signale tout échec de test lorsque le test est activé.

Vous pouvez consulter le code complet de ces exemples sur GitHub.