

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

# Genera simulaciones automáticamente utilizando `mockall` el AWS SDK para Rust
<a name="testing-automock"></a>

 AWS SDK para Rust Proporciona varios enfoques para probar el código con Servicios de AWS el que interactúa. Puede generar automáticamente la mayoría de las implementaciones simuladas que necesitan las pruebas utilizando el popular `[automock](https://docs.rs/mockall/latest/mockall/attr.automock.html)` de la caja `[mockall](https://docs.rs/mockall/latest/mockall)`.

En este ejemplo se prueba un método personalizado denominado `determine_prefix_file_size()`. Este método llama a un método de contenedor `list_objects()` personalizado que llama a Amazon S3. Al simular `list_objects()`, el método `determine_prefix_file_size()` se puede probar sin contactar con Amazon S3. 

1. En la línea de comandos del directorio del proyecto, agregue la caja `[mockall](https://docs.rs/mockall/latest/mockall)` como dependencia:

   ```
   $ cargo add --dev mockall
   ```

   Al usar la [opción](https://doc.rust-lang.org/cargo/commands/cargo-add.html) `--dev`, se agrega la caja a la sección `[dev-dependencies]` del archivo `Cargo.toml`. Como [dependencia de desarrollo](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#development-dependencies), no se compila ni se incluye en el binario final que se utiliza para el código de producción.

   Este código de ejemplo también utiliza Amazon Simple Storage Service como el Servicio de AWS de ejemplo.

   ```
   $ cargo add aws-sdk-s3
   ```

   De esta forma, se agrega la caja a la sección `[dependencies]` del archivo `Cargo.toml`.

1. Incluya el módulo `automock` de la caja `mockall`. 

   Incluya también cualquier otra biblioteca relacionada con la Servicio de AWS que esté probando, en este caso, Amazon S3.

   ```
   use aws_sdk_s3 as s3;
   #[allow(unused_imports)]
   use mockall::automock;
   
   use s3::operation::list_objects_v2::{ListObjectsV2Error, ListObjectsV2Output};
   ```

1. A continuación, agregue el código que determina cuál de las dos implementaciones de la estructura de encapsulador de Amazon S3 de la aplicación debe utilizarse. 
   + La real, escrita para acceder a Amazon S3 a través de la red.
   + La implementación simulada generada por `mockall`.

   En este ejemplo, a la que está seleccionada se le asigna el nombre `S3`. La selección es condicional y se basa en el atributo `test`:

   ```
   #[cfg(test)]
   pub use MockS3Impl as S3;
   #[cfg(not(test))]
   pub use S3Impl as S3;
   ```

1. La `S3Impl` estructura es la implementación de la estructura contenedora de Amazon S3 a la que realmente envía las solicitudes. AWS
   + Cuando las pruebas están habilitadas, este código no se usa porque la solicitud se envía a la simulación y no a AWS. El atributo `dead_code` le indica al linter que no informe de ningún problema si no se utiliza el tipo `S3Impl`.
   +  El `#[cfg_attr(test, automock)]` condicional indica que cuando las pruebas están habilitadas, se debe establecer el atributo `automock`. Esto indica a `mockall` que hay que generar una simulación de `S3Impl` al que se le asignará el nombre `Mock{{S3Impl}}`.
   + En este ejemplo, el método `list_objects()` es la llamada que quiere que se simule. `automock` creará automáticamente un método `expect_{{list_objects()}}` para usted. 

   ```
   #[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
       }
   }
   ```

1. Cree las funciones de prueba en un módulo denominado `test`.
   + El `#[cfg(test)]` condicional indica que `mockall` debe crear el módulo de prueba si el atributo `test` es `true`.

   ```
   #[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);
       }
   }
   ```
   + Cada prueba utiliza `let mut mock = MockS3Impl::default();` para crear una instancia `mock` de `MockS3Impl`. 
   + Usa el método `expect_list_objects()` de la simulación (que `automock` creó automáticamente) para establecer el resultado esperado cuando el método `list_objects()` se usa en otra parte del código.
   + Una vez establecidas las expectativas, las usa para probar la función llamando a `determine_prefix_file_size()`. El valor devuelto se comprueba para confirmar que es correcto, utilizando una aserción.

1. La función `determine_prefix_file_size()` usa el encapsulador de Amazon S3 para obtener el tamaño del archivo de prefijos:

   ```
   #[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)
   }
   ```

El tipo `S3` se usa para llamar a las funciones encapsuladas de SDK para Rust sean compatibles con `S3Impl` y `MockS3Impl` cuando se realizan solicitudes HTTP. La simulación generada automáticamente por `mockall` informa de cualquier error en las pruebas cuando estas están habilitadas.

Puede [ver el código completo de estos ejemplos](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/rustv1/examples/testing) en. GitHub