Como posso testar uma unidade que exige uma chamada de serviço da web?

21

Estou tentando testar uma classe que chama alguns serviços web do Hadoop. O código tem praticamente a forma:

method() {
    ...use Jersey client to create WebResource...
    ...make request...
    ...do something with response...
}

por exemplo, existe um método de criação de diretório, um método de criação de pasta etc.

Como o código está lidando com um serviço da Web externo sobre o qual eu não tenho controle, como posso fazer o teste unitário? Eu poderia tentar zombar do cliente / respostas do serviço web, mas isso quebra a diretriz que vi muito recentemente: "Não zombe de objetos que você não possui". Eu poderia configurar uma implementação fictícia de serviço da Web - isso ainda constituiria um "teste de unidade" ou seria um teste de integração? Simplesmente não é possível realizar testes de unidade nesse nível mais baixo - como um profissional de TDD faria isso?

Chris Cooper
fonte
5
Onde você viu orientação para não zombar de coisas que não possui? Essa parece ser uma grande razão pela qual você deve zombar das coisas ...
Thomas Owens
1
Eu já li isso em muitos blogs, um dos quais faz referência a amazon.com/Growing-Object-Oriented-Software-Guided-Tests/dp/…, que eu sei que é um livro bem considerado ( blog.8thlight. com / eric-smith / 2011/10/27 / thats-not-yours.html , mockobjects.com/2007/04/test-smell-everything-is-mocked.html )
Chris Cooper
1
@ ChrisCooper: Devo salientar que o último link está muito desatualizado (a partir de 2007). Muita coisa mudou desde então. Tenho a sensação do post que zombeteiro era muito mais difícil na época em que você teve para realmente implementar o comportamento ao invés de simplesmente usando uma plataforma de simulacros ou metaprogramming para configurar valores de retorno ...
c_maker

Respostas:

41

Na minha opinião, você deve zombar das chamadas do serviço da web se for um teste de unidade, em oposição a um teste de integração.

Seu teste de unidade não deve testar se o serviço da web externo está funcionando ou se a sua integração com ele está correta. Sem ser muito dogmático sobre o TDD, observe que um efeito colateral de transformar seu teste de unidade em um teste de integração é que ele provavelmente será mais lento e você desejará testes de unidade rápidos .

Além disso, se o serviço da web estiver temporariamente fora do ar ou funcionando incorretamente, isso deve causar uma falha no seu teste de unidade? Não parece certo. Seu teste de unidade deve falhar por apenas um motivo: se houver um erro no código nessa "unidade".

A única parte do código relevante aqui é ...do something with response.... Zombe do resto.

Andres F.
fonte
2
Lembre-se de que você precisará manter sua assinatura de objeto simulada e retornar valores sincronizados com os gerados pelo serviço da web do Hadoop durante as alterações inevitáveis ​​nesse serviço.
pcurry
Raramente vale a pena testar componentes prontos para uso como o Hadoop. Mas se você estiver chamando um serviço da web personalizado fornecido por outra equipe ou organização, poderá escrever um teste para ele em legítima defesa. Dessa forma, quando as coisas dão errado, você pode verificar rapidamente se o problema é o seu código ou o serviço da web. Este não é um teste de unidade para executar automaticamente; é um diagnóstico para executar conforme necessário.
Kevin Cline
@kevincline Concordo plenamente com a necessidade dos testes que você propõe e, de fato, eu os escrevo no meu trabalho diário e provaram ser úteis. Mas eles são, por definição, NÃO testes de unidade, que era a questão :) Considere o seguinte: se for um teste de unidade e o código falhar porque o serviço da web foi alterado, qual é a "unidade" que você está testando? O que exatamente falhou? Você não está testando isoladamente, conforme necessário pelo teste de unidade.
Andres F.
1
@AndresF .: Acho que estamos de acordo violenta: "Este [diagnóstico] não é um teste de unidade ..."
Kevin Cline
@kevincline Right! Eu interpretei mal o seu comentário, desculpe!
Andres F.Fev
5

Discordo de "não zombe de objetos que você não possui" quando estiver testando a unidade.

Zomba do propósito da existência é o fato de que haverá módulos, bibliotecas, classes que não possuiremos.

Minha sugestão para o seu cenário é simular a chamada de serviço da web.

Configure o mock de maneira que ele retorne dados ao seu módulo.
Certifique-se de cobrir todo o cenário, por exemplo, quando os dados retornados são nulos, quando os dados retornados são válidos etc.

E para o código que você possui, sua responsabilidade como desenvolvedor é garantir que o código que você está criando seja executado conforme o esperado em todos os cenários.

Venu b
fonte
1

Eu usaria algo como EasyMock para este teste. As estruturas de zombaria são uma maneira ideal de remover dependências externas de uma classe e oferecem controle total sobre o resultado de dependências externas durante os testes. Para estender um pouco o seu exemplo:

class WebClass {

private WebServiceInterface webserviceInterface;

    void method(){
        R result = webServiceInterface.performWebServiceCall();
        ... do something with result
    }

    public void setWebServiceInterface(WebServiceInterface webServiceInterface){
        this.webServiceInterface = webServiceInterface;
    }
}


interface WebServiceInterface {

   R performWebServiceCall();

}


class WebClassTest {

private WebServiceInterface mock;    
private R sampleResult = new R();

    @Before
    public void before(){
        mock = EasyMock.createMock(WebServiceInterface.class);
    }


    @Test
    public void test() {
        WebClass classUnderTest = new WebClass();
        EasyMock.expect(mock.performWebServiceCall()).andReturn(sampleResult);
        classUnderTest.setWebServiceInterface(mock);
        classUnderTest.method();
        EasyMock.verify(mock);
    }
}

A primeira coisa que você precisa fazer é extrair a lógica da sua classe em que você usa Jersey para obter um WebResource e chamar o serviço da Web em uma classe separada. A criação de uma interface para esta classe permitirá que você crie uma simulação para a qual você pode ditar o comportamento.

Depois que essa interface é criada, você pode criar uma simulação usando o EasyMock, que retornará um objeto especificado de acordo com o seu caso de teste. O exemplo acima é uma simplificação de como estruturar um teste simulado básico e como sua interface funcionará.

Para obter mais informações sobre estruturas de simulação, consulte esta pergunta . Além disso, este exemplo pressupõe o uso de Java, mas as estruturas de simulação estão disponíveis em todas as linguagens e, embora elas sejam implementadas de maneira diferente, elas geralmente funcionam da mesma maneira

Richard
fonte
1

Zombarias são aceitáveis ​​neste caso, mas você não precisa. Em vez de teste de unidade method(), em vez disso, teste apenas a parte que lida com a resposta.

Extraia uma função que ResponseDataexecute (de qualquer tipo que seja apropriado) e execute a ação.

Em vez de zombar, agora você apenas constrói um objeto ResponseData e o passa.

Você pode deixar a chamada do serviço para testes completos de integração - que abrangerão method()no total

Daenyth
fonte
0

O que eu fiz e funciona:

  1. Tenha todos os serviços de chamada de código da web por meio de proxy.
  2. O proxy chama uma classe que sabe estaticamente se estamos usando proxy ou não e redireciona de acordo. As zombarias são apenas HashMaps que, para cada solicitação, retornam uma determinada resposta.
  3. Execute os testes várias vezes nesta ordem:

3.1 Primeiro, todos os serviços da web são testados. De cada máquina, até as máquinas do desenvolvedor. Esses são os verdadeiros serviços da web, mas rodando em ambiente de desenvolvimento. Isso significa que os serviços da web nunca podem ser desativados ou responder a valores errados, porque, de outra forma, todo desenvolvedor reclama que não pode compilar.

3.2 Em seguida, todos os testes de unidade internos ao aplicativo são executados. Isso significa que todos os serviços da Web são ridicularizados e testados, executando os mesmos testes do 3.1 (e devem passar também; caso contrário, as zombarias estão erradas) e sendo invocados pelo aplicativo real como se estivessem realmente sendo usados. Se as zombarias estiverem erradas, você poderá executar o teste na versão 3.1 e registrar esses valores (solicitação, resposta) em um HashMap.

3.3 Em seguida, os mesmos testes do 3.2 são executados, mas desta vez contra os serviços da Web reais em execução no ambiente de desenvolvimento.

Depois que tudo isso foi concluído, para o ambiente de produção real, você só precisa fornecer o endereço real de cada serviço da web. Espero que isso não exija muita alteração na configuração.

Guillermo Schwarz
fonte