Como testar código que depende de APIs complexas (Amazon S3, por exemplo)?

13

Estou com dificuldades para testar um método que carrega documentos para o Amazon S3, mas acho que essa pergunta se aplica a qualquer API não trivial / dependência externa. Eu só vim com três soluções em potencial, mas nenhuma parece satisfatória:

  1. Execute o código, faça o upload do documento, verifique com a API da AWS se ele foi carregado e exclua-o no final do teste. Isso tornará o teste muito lento, custará dinheiro toda vez que o teste for executado e nem sempre retornará o mesmo resultado.

  2. Zombar S3. Isso é super cabeludo, porque eu não tenho idéia sobre os componentes internos desse objeto e parece errado porque é muito complicado.

  3. Apenas certifique-se de que MyObject.upload () seja chamado com os argumentos corretos e confie em que estou usando o objeto S3 corretamente. Isso me incomoda, porque não há como saber com certeza se usei a API S3 corretamente apenas nos testes.

Eu verifiquei como a Amazon testa seu próprio SDK e eles zombam de tudo. Eles têm um auxiliar de 200 linhas que faz a zombaria. Não acho prático fazer o mesmo.

Como eu resolvo isso?

carregado com mola
fonte
Não é uma resposta, mas, na prática, usamos as três abordagens que você expôs.
jlhonora

Respostas:

28

Há duas questões que precisamos analisar aqui.

A primeira é que você parece observar todos os seus testes da perspectiva do teste de unidade. Os testes de unidade são extremamente valiosos, mas não são os únicos tipos de testes. Na verdade, os testes podem ser divididos em várias camadas diferentes, desde testes de unidade muito rápidos a testes de integração menos rápidos , até testes de aceitação ainda mais lentos . (Pode haver ainda mais camadas divididas, como testes funcionais .)

A segunda é que você está misturando chamadas para código de terceiros com sua lógica de negócios, criando desafios de teste e possivelmente tornando seu código mais frágil.

Os testes de unidade devem ser rápidos e devem ser executados com frequência. A zombaria de dependências ajuda a manter esses testes em execução rapidamente, mas pode introduzir furos na cobertura se a dependência mudar e a simulação não mudar. Seu código pode ser quebrado enquanto seus testes ainda são executados em verde. Algumas bibliotecas de simulação irão alertá-lo se a interface da dependência mudar, outras não.

Os testes de integração, por outro lado, são projetados para testar as interações entre componentes, incluindo bibliotecas de terceiros. As zombarias não devem ser usadas nesse nível de teste, porque queremos ver como o objeto real interage. Como estamos usando objetos reais, esses testes serão mais lentos e não os executaremos tão frequentemente quanto nossos testes de unidade.

Os testes de aceitação olham para um nível ainda mais alto, testando se os requisitos para o software são atendidos. Esses testes são executados em todo o sistema completo que seria implantado. Mais uma vez, nenhuma zombaria deve ser usada.

Uma orientação que as pessoas consideraram valiosa em relação às zombarias é não zombar dos tipos que você não possui . A Amazon é proprietária da API do S3 para garantir que ela não seja alterada abaixo deles. Você, por outro lado, não possui essas garantias. Portanto, se você zombar da API do S3 em seus testes, ela pode alterar e quebrar seu código, enquanto todos os testes são exibidos em verde. Então, como fazemos o código de teste unitário que usa bibliotecas de terceiros?

Bem, nós não. Se seguirmos a diretriz, não podemos zombar de objetos que não possuímos. Mas ... se possuímos nossas dependências diretas, podemos zombar delas. Mas como? Criamos nosso próprio wrapper para a API S3. Podemos fazer com que ela se pareça muito com a API S3, ou podemos atender melhor às nossas necessidades (de preferência). Podemos até torná-lo um pouco mais abstrato, digamos, em PersistenceServicevez de um AmazonS3Bucket.PersistenceServiceseria uma interface com métodos como #save(Thing)e #fetch(ThingId), os tipos de métodos que gostaríamos de ver (estes são exemplos, você pode realmente querer métodos diferentes). Agora podemos implementar uma PersistenceServiceAPI S3 (digamos a S3PersistenceService), encapsulando-a para longe do código de chamada.

Agora, o código que chama a API S3. Precisamos substituir essas chamadas por chamadas para um PersistenceServiceobjeto. Usamos injeção de dependência para passar nossa PersistenceServicepara o objeto. É importante não pedir um S3PersistenceService, mas pedir um PersistenceService. Isso nos permite trocar a implementação durante nossos testes.

Todo o código que costumava usar a API S3 agora usa agora o nosso PersistenceService, e o nosso S3PersistenceServiceagora faz todas as chamadas para a API S3. Em nossos testes, podemos zombar PersistenceService, já que o possuímos, e usá-lo para garantir que nosso código faça as chamadas corretas. Mas agora isso deixa como testarS3PersistenceService . Ele tem o mesmo problema de antes: não podemos testá-lo sem chamar o serviço externo. Então ... nós não fazemos testes unitários. Nós poderia zombar as dependências S3 API, mas isto nos daria pouco ou nenhuma confiança adicional. Em vez disso, temos que testá-lo em um nível superior: testes de integração.

Isso pode parecer um pouco preocupante, dizendo que não devemos testar uma parte do nosso código, mas vejamos o que realizamos. Tínhamos um monte de código em todo o lugar que não podíamos testar em unidade que agora pode ser testado em unidade através doPersistenceService . Temos nossa confusão de bibliotecas de terceiros confinada a uma única classe de implementação. Essa classe deve fornecer a funcionalidade necessária para usar a API, mas não possui nenhuma lógica comercial externa anexada a ela. Portanto, uma vez escrito, deve ser muito estável e não deve mudar muito. Podemos confiar em testes mais lentos que não executamos com frequência porque o código é estável.

O próximo passo é escrever os testes de integração para S3PersistenceService. Eles devem ser separados por nome ou pasta, para que possamos executá-los separadamente de nossos testes rápidos de unidade. Os testes de integração geralmente podem usar as mesmas estruturas de teste que os testes de unidade, se o código for suficientemente informativo; portanto, não precisamos aprender uma nova ferramenta. O código real para o teste de integração é o que você escreveria para sua Opção 1.

cbojar
fonte
a questão é como você executa os testes de integração ou e2e para API que você expõe. Você não pode zombar do PersistenceService por razões óbvias. Ou eu mal entendido alguma coisa, ou adicionar outra camada entre a API do aplicativo e AWS API, dá-lhe nada mais do que ter tempo mais fácil fazer testes de unidade
Yerken
@ Yerken Enquanto penso nisso, tenho certeza de que poderia preencher outra longa resposta a essa pergunta. Isso pode até valer a pena para você, porque você pode receber mais do que apenas minha resposta.
Cbojar
4

Você precisa fazer as duas coisas.

Executar, fazer upload e excluir é um teste de integração. Ele faz interface com um sistema externo e, portanto, espera-se que funcione lentamente. Provavelmente não deve fazer parte de todas as compilações feitas localmente, mas deve fazer parte de uma compilação de IC ou compilação noturna. Isso compensa a lentidão desses testes e ainda fornece o valor de tê-lo testado automaticamente.

Você também precisa de unittests que executam mais rapidamente. Como geralmente é inteligente não depender muito de um sistema externo (para que você possa trocar implementações ou alternar), provavelmente você deve tentar escrever uma interface simples no S3 com a qual possa codificar. Zombe dessa interface em unittests para que você possa ter unittests de execução rápida.

Os primeiros testes verificam se o seu código realmente funciona com o S3, os segundos que seu código chama corretamente o código que fala com o S3.

JDT
fonte
2

Eu diria que isso depende da complexidade do seu uso da API .

  1. Você definitivamente precisa fazer pelo menos alguns testes que realmente chamam a API S3 e confirmam que funcionou de ponta a ponta.

  2. Você também definitivamente precisa fazer testes adicionais que na verdade não chamam a API, para poder testar seu próprio software adequadamente sem invocar a API o tempo todo.

A questão que permanece é: você precisa zombar da API?

E acho que depende de quanto você faz com isso. Se você está realizando apenas uma ou duas ações simples, acho que não precisa se dar ao trabalho de criar um modelo. Eu ficaria satisfeito em apenas verificar meu uso das funções e fazer alguns testes ao vivo.

No entanto, se o uso for mais complexo, com cenários diferentes e variáveis ​​diferentes que podem afetar os resultados, você provavelmente precisará zombar dele para fazer testes mais detalhados.


fonte
1

Além das respostas anteriores, a principal pergunta é se (e como) você deseja zombar da API do S3 para seus testes.

Em vez de zombar manualmente de respostas individuais do S3, você pode tirar proveito de algumas estruturas de zombaria existentes muito sofisticadas. Por exemplo moto fornece uma funcionalidade muito semelhante à API S3 real.

Você também pode dar uma olhada no LocalStack , uma estrutura que combina ferramentas existentes e fornece um ambiente de nuvem local totalmente funcional (incluindo o S3) que facilita o teste de integração.

Embora algumas dessas ferramentas sejam escritas em outras linguagens (Python), deve ser fácil ativar o ambiente de teste em um processo externo a partir de seus testes em, por exemplo, Java / JUnit.

whummer
fonte