As bibliotecas de cliente do Cloud para Rust oferecem uma maneira de criar stubs de implementações de cliente reais para que você possa injetar uma simulação para testes. É possível usar simulações para escrever testes de unidade controlados e confiáveis que não fazem chamadas de rede nem geram cobranças.
Neste documento, você vai aprender sobre os seguintes tópicos:
- Como escrever interfaces testáveis usando o cliente
Storagedo Cloud Storage. - Como simular operações de leitura.
- Como simular operações de gravação.
- Por que o design do cliente do
Storageé diferente de outros clientes do Google Cloud.
Dependências
Adicione a biblioteca de cliente do Cloud Storage ao arquivo Cargo.toml:
cargo add google-cloud-storage
Para compilar e executar os testes de unidade neste documento, adicione as seguintes dependências de desenvolvimento:
cargo add --dev anyhow bytes mockall
cargo add --dev tokio --features macros,rt-multi-thread
Criar interfaces testáveis
Se você não fizer testes de unidade no código de armazenamento, poderá escrever interfaces diretamente em
termos de Storage, cujo parâmetro de tipo de stub padrão é a implementação de produção
do cliente:
Para testar seu código com um cliente simulado, escreva suas interfaces em termos de um
parâmetro de tipo genérico T restrito por gcs::stub::Storage + 'static:
Simular operações de leitura
Para simular solicitações de read_object, siga estas etapas:
Escreva uma função de aplicativo que use um cliente
&Storage<T>genérico. Por exemplo, a função a seguir faz o download de um objeto e conta quantos caracteres de nova linha ele contém:Crie uma função auxiliar que construa um fluxo de bytes
ReadObjectResponsefalso transmitindo um payload paraReadObjectResponse::from_source. Esse método aceita os mesmos tipos de payload queStorage::write_object:Defina uma struct simulada que implemente
gcs::stub::Storage. Este documento usa o cratemockall, mas é possível usar um framework de simulação diferente nos testes:Escreva um teste de unidade que defina as expectativas no simulador, construa um cliente usando
Storage::from_stube chamecount_newlines:
Simular operações de gravação
Para simular solicitações de write_object, siga estas etapas:
Escreva uma função de aplicativo que faça upload de um objeto usando um cliente
&Storage<T>genérico:Usando o struct
MockStoragedefinido na seção anterior, escreva um teste de unidade que configure as expectativas no simulador, construa um cliente usandoStorage::from_stube chameupload:
Detalhes da expectativa
Como a função upload chama send_unbuffered, defina a expectativa no
método expect_write_object_unbuffered correspondente:
A macro mockall::mock! trata cada instanciação genérica como um método
distinto. Especifique o tipo de payload concreto, Payload<BytesSource>, na
assinatura de fechamento para que o compilador possa corresponder à expectativa do método:
Justificativa do design
As seções a seguir explicam por que o cliente Storage usa um parâmetro de tipo genérico
para o stub em vez de envio dinâmico.
Outros clientes
A maioria dos clientes, como StorageControl, mantém internamente uma implementação compatível com dyn
e com contagem de referências (Arc) do trait de stub. Eles usam
a expedição dinâmica para encaminhar solicitações do cliente para o stub subjacente,
que pode ser a implementação de produção ou um modelo.
Como esses clientes usam o envio dinâmico, o compilador não precisa resolver o tipo de stub concreto no tempo de compilação, e as structs do cliente não precisam de um parâmetro de tipo genérico para o stub.
O cliente Storage
No Rust, uma característica compatível com dyn não pode ter métodos genéricos ou retornar valores impl Future
não empacotados:
- Os métodos
write_object_bufferedewrite_object_unbufferedsão genéricos em relação ao tipo de origem do payload. - Os métodos de característica
gcs::stub::Storageretornamimpl Futurediretamente, em vez de encapsular o futuro retornado.
O uso de despacho dinâmico para o cliente Storage exigiria o boxing de payloads
e futuros em cada chamada, adicionando alocações de heap e sobrecarga de despacho
dinâmico no caminho de dados. Para evitar essa sobrecarga, o cliente Storage usa um
parâmetro de tipo para uma implementação concreta da característica gcs::stub::Storage.
Código do aplicativo e pacote de testes completos
O código a seguir combina as funções do aplicativo e os testes de unidade em um exemplo completo:
Execute cargo test para executar test_count_lines e test_upload:
cargo test