Testando um cliente REST em um servidor REST. Como fazer luminárias?

10

Ao escrever testes de unidade, é comum usar acessórios: poucos dados testáveis, para que possamos dizer: 1. Faça com que todos os clientes incluam Willy Wonka. 2. Exclua o cliente 3 e agora obtenha clientes não deve mais incluir Willy Wonka.

Isso é bom para testes de unidade. Use configuração / desmontagem para recarregar os equipamentos ou reverter a transação. Portanto, os testes de criação, atualização e exclusão são feitos dentro de uma transação . Os novos dados temporários duram apenas a duração desse teste e são redefinidos.

Mas e quando separamos o servidor REST do cliente REST?

Queremos garantir que nosso cliente REST não esteja apenas lendo corretamente, mas criando, atualizando e excluindo corretamente.

Não consegui encontrar exemplos ou sugestões de como fazer isso em um servidor REST de teste remoto.

Supondo que eu tenho um servidor REST de teste que serve apenas equipamentos. Toda a natureza apátrida do HTTP significa que seria difícil enviar uma mensagem do tipo "BEGIN TRANSACTION" e "ROLLBACK TRANSACTION" ou "RELOAD FIXTURES", certo?

Não posso ser o primeiro a querer fazer isso, então sinto que preciso de uma maneira diferente de pensar sobre isso.

Alguma sugestão?

sivers
fonte
Talvez, como é um servidor de teste, você possa ter um terminal que recarregará os equipamentos?
David Radcliffe
Se o seu principal problema é trazer o servidor de teste de volta a um estado predefinido, por que você não adiciona algum tipo de função de teste especial como "RELOAD TESTDATA" à sua API restante para fazer o que você deseja? Obviamente, você deve garantir que esse tipo de chamada à API não esteja disponível na produção.
Doc Brown

Respostas:

7

Idealmente, os sistemas de software têm limites e interfaces de sistema bem definidos entre eles. Os serviços REST são bons exemplos disso.

Para esse fim, eu recomendaria não fazer o que você está tentando fazer.

Especificamente:

Queremos garantir que nosso cliente REST não esteja apenas lendo corretamente, mas criando, atualizando e excluindo corretamente.

O que eu sugeriria é:

  • Construindo testes para seu cliente REST, para garantir que ele se comporte corretamente, com entrada e saída específicas. Conta para valores bons (esperados) e ruins (inesperados).

  • Construindo testes para o serviço REST (se você o controlar), para se comportar de acordo com a função pretendida

  • Mantenha os testes próximos ao domínio do problema, para que eles possam ajudar a orientar o design e o desenvolvimento do que é importante nesse contexto

briandoll
fonte
3
Você descarta toda a idéia de testes de integração aqui de maneira bastante casual. Eu não acho que essa abordagem seja informada da prática.
Febeling 09/03
Obrigado a todas as sugestões úteis. Também pelo Twitter, recebi ótimas sugestões para experimentar o ruby ​​gem "webmock" e semelhante a zombar da resposta do servidor da API REST. Também concordo com o "febeling" de que o que estou descrevendo parece ser mais um teste de integração, então analisarei isso separadamente. Mais uma vez obrigado a todos. - Derek
Sivers
zombar de uma API é uma ótima maneira de resolver o problema. Mas como você garante que a API ridicularizada == API real?
FrEaKmAn
4

Dois ângulos a serem lembrados aqui:

  • Você está testando seu código ou o encanamento? Presumindo que você esteja usando um serviço bem conhecido e uma pilha de clientes, provavelmente poderá presumir com segurança seus testadores e milhares de usuários geralmente garantirão que não haja um erro fundamental nos fundamentos.
  • Por que seus testes não são idempotentes? Crie uma maneira de gravar dados de não produção ou em um terminal diferente. Escolha um padrão de nomenclatura previsível. Pré-carregue o banco de dados do servidor restante antes dos testes. E provavelmente existem mais algumas maneiras de fazer isso acontecer - o método é realmente tático e deve depender da natureza do aplicativo.
Wyatt Barnett
fonte
1

Como já foi dito, se você estiver testando um cliente, não precisará ir tão longe quanto criar, excluir etc. no servidor. Na maioria das vezes, você nem precisa zombar de um servidor. Você realmente só precisa ter certeza de que está fazendo as solicitações corretas e manipulando as respostas corretamente, seja em Ruby, Python, PHP ou qualquer outra coisa, em algum momento seu cliente provavelmente usará um método de uma biblioteca HTTP para uma solicitação e basta zombar desse método, verificar como ele é chamado e retornar um resultado de teste.

Veja um hipotético cliente Python que é usado urllib2para fazer solicitações. Você provavelmente tem algum método no cliente, vamos chamá-lo get(), que tem uma chamada para urllib2.Request()ele. Você realmente só precisa zombar da chamada da sua própria turma get().

@patch('your.Client.get')
def test_with_mock(self, your_mock):
    your_mock.return_value({'some': 'json'})
    test_obj = your.Client.get_object(5)
    your_mock.assert_called_with('/the/correct/endpoint/5')

Este exemplo muito simplificado usa a biblioteca Mock do Python para testar uma your.Clientclasse hipotética com um get_object()método que gera a URL correta para obter algo de alguma API. Para fazer a solicitação, o cliente chama seu get()método com esse URL. Aqui, esse método é ridicularizado ( your.Client.geté "corrigido" para que esteja sob o controle de your_mock) e o teste verifica se o terminal correto foi solicitado.

O método simulado retorna a resposta JSON configurada ( your_mock.return_value) que o cliente deve manipular e você faria outras afirmações para testar se tratou os dados esperados da maneira esperada.

Sean Redmond
fonte
Mas como você tem certeza de que, quando está fazendo a solicitação "certa", é uma solicitação real de direito (em produção)? Porque se eu entender sua sugestão, se a API mudar ou quebrar, seus testes ainda funcionarão. Enquanto na produção é uma história totalmente diferente.
FrEaKmAn
1

O que você descreve é ​​um cenário de teste de integração. Estes geralmente são um pouco difíceis de configurar e derrubar. Isso os torna lentos para executar e muitas vezes quebradiços.

A abordagem com equipamentos é igualmente desajeitada e desajeitada, mas é a maneira padrão de algumas estruturas, como o Rails, e já é suportada. Eles precisam do caso de teste abstrato ou algo semelhante para preparar o banco de dados com acessórios. (Cuidado com a nomeação incomum de categorias de teste no Rails, os testes de unidade com equipamentos de banco de dados também são estritamente testes de integração.)

A maneira como eu abordaria seu cenário é aceitar ter controle específico do teste sobre o estado do aplicativo da API ou seu banco de dados. Você pode ter pontos de extremidade adicionais para configuração e desmontagem, que estão presentes apenas no ambiente de teste. Ou, alternativamente, você fala com o banco de dados (ou o que quer que esteja usando) nas costas do seu aplicativo / API.

Se você acha que isso é muito esforço (no sentido de indevido), considere que a abordagem com acessórios para bancos de dados faz exatamente isso: usando meios adicionais específicos de teste para manipular o banco de dados ou o estado do aplicativo.

Não acho que essa discussão tenha a ver com a natureza sem estado do HTTP, no entanto. HTTP é sem estado, mas o aplicativo definitivamente não é, na maioria dos casos. Parece um pouco como você procurando por rigor REST. Você também pode ter todos os recursos totalmente criativos, legíveis e excluídos. Nesse caso, você pode simplesmente fazer todas as configurações e desmontagens por meios regulares da API. Porém, isso geralmente não é feito na prática, porque você não deseja incluir determinadas operações a partir do entendimento comercial de seu aplicativo, pelo menos não fora do ambiente de teste.

desagradável
fonte
1

Monkey Patch

No meu trabalho, fazemos ATDD usando uma estrutura xUnit existente e chamadas de rede de correção de macaco entre o cliente e o servidor. No mesmo espaço de processo que carregamos o cliente, o patch do macaco é chamado de rede para a parte superior do código da pilha do servidor REST. Todas as chamadas são emitidas do cliente como normalmente seriam, e o código do servidor recebe as solicitações exatamente como elas normalmente apareceriam.

Benefícios

  • não é necessário sincronizar com a inicialização do servidor (porque não há servidor)
  • utilize o método clássico de configuração e desmontagem de unidades para gerenciar itens como acessórios
  • capacidade de usar zombarias / tocos e outras manchas de macaco para um controle mais refinado do teste
  • pode ser escrito usando uma moldura xUnit

Tradeoffs

  • não expõe interações / problemas de vários processos (bloqueio, falta de recursos etc.)
  • não expõe problemas de vários servidores (serialização de dados, estilo de cluster)
  • não expõe problemas de rede porque isso é simulado (acesso, erros de tempo limite, etc.)
dietbuddha
fonte