Como testar um método que não é tanto quanto uma unidade, porque é mais uma classe de 'orquestrador' / 'processo' / 'controlador' / 'coordenação' [duplicado]

4

Eu quero testar um método que não é tanto quanto uma unidade, porque é mais uma classe de 'orquestrador' / 'processo' / 'controlador' / 'coordenação'.

Este é o caso:

Eu tenho quatro turmas testadas:

  • Um é um serviço de dados que pode ler / gravar dados do banco de dados
  • O segundo é um serviço de textos que pode criar conteúdo para e-mails / mensagens etc.
  • O terceiro é um serviço de e-mail que pode enviar e-mail
  • Quarta é uma classe dat pode criar tarefas para usuários em nosso sistema (tarefas são coisas eles deveriam fazer)

Agora criei uma nova turma, que envia um e-mail para todas as pessoas que estão atrasadas no pagamento de uma fatura. Ele lê os dados com o serviço de dados, cria o texto apropriado com o serviço de textos, envia um e-mail com o serviço de e-mail, grava o novo status de fatura com o serviço de dados e cria uma tarefa quando o envio de uma fatura específica falha.

Agora, essa nova classe é minha classe "orquestradora" ou "processo" ou "controladora" ou "coordenação".

Eu tenho muito esse tipo de aula em nosso aplicativo porque tentamos fazer com que nossas classes (como os dados / email / textoservice) sejam tão pequenas quanto possível, assim quando 'o trabalho tem que ser feito', como neste caso o ' mail todas as pessoas que estão atrasadas com o pagamento ', criamos uma nova classe' orquestrador 'ou' processo 'ou' controlador 'ou' coordenação '.

Acho que tenho esse tipo de classe para a maioria das minhas ações em meus webcontrollers porque a maioria das entradas enviadas pelo navegador envolve a coordenação entre várias classes (menores)

Agora, como testo essas classes / métodos?

Eu costumava zombar de todas as 4 classes no meu teste e verificar no final que as classes são chamadas, e na ordem correta.

Mas cada vez mais eu leio que você não deve fazer isso, porque então você testa o funcionamento interno de um método, e quando você refaz esse método, o teste falha. Então eu devo testar os resultados, não para o trabalho interno. Mas esse método é inválido, então há muitos resultados para verificar. A única coisa que posso pensar, por exemplo, para verificar é: o email é enviado? Mas a única maneira de verificar isso é verificar se o serviço de e-mail é chamado, mas voltarei a testar os componentes internos.

Eu não vejo este tipo de exemplos nos livros unit testing / tdd, porque na maior parte do tempo eles só trabalham com classes pequenas como a classe calculadora, mas raramente vejo exemplos para 'orquestrador' aulas como estou descrevendo, mas que ocorrem muito no meu código.

Para aqueles que acham que é uma duplicata: acho que as respostas aqui fornecem muito mais informações do que a única resposta na outra pergunta. Essa outra questão foi respondida com um teste de integração em mente, e minha pergunta é sobre testes de unidade, não testes de integração. Por isso, não posso concordar com o ponto de resposta duplicado.

    
por Michel 29.01.2015 / 09:22
fonte

4 respostas

15

A partir do que você descreve, eu diria que a maneira como você tem feito isso - ridicularizar os colaboradores - é a melhor abordagem. Pode ser que você esteja exagerando na especificação - por exemplo, exigindo uma ordem que não é realmente exigida pelas necessidades do negócio; Se esse for o caso, você pode melhorar os requisitos de pedidos dos seus testes. Mas basicamente para o coordenador você quer saber que:

  • dado um serviço de dados que diz "essas pessoas estão atrasadas"
  • dado um serviço de texto que gera e-mail do formulário "ei, você está atrasado"
  • dado um serviço de e-mail que envia para uma lista de destinatários
  • ele enviará uma solicitação ao serviço de e-mail para enviar "ei, você está atrasado" para o conjunto de pessoas mencionado acima.

Se estiver fazendo isso (e algumas outras coisas que você mencionou que eu não estou me incomodando em especificar aqui), então, no que lhe diz respeito, sua classe de coordenadores está funcionando. Esse é um teste fácil de escrever com brincadeiras, limpo e comunicativo. Essa é a maneira que eu recomendo.

    
por 29.01.2015 / 10:02
fonte
5

Não é absolutamente da responsabilidade do seu coordenador enviar e-mails, apenas para pedir que ele seja enviado. Existem muitas razões pelas quais um email pode não ser enviado ou atrasado. É da responsabilidade da sua classe de correio para

  1. enviar e-mail válido
  2. rejeitar e-mails inválidos
  3. Registre o que ele fez (incluindo falhas inesperadas para enviar e-mails válidos)

Todo o seu coordenador tem que fazer, no que diz respeito ao correio, é

  1. Solicitar envio de e-mail
  2. Reaja adequadamente se a chamada para o objeto de e-mail retornar uma falha.
  3. Registre o que aconteceu

Se os seus testes para a sua classe de e-mail forem abrangentes, você deve ter certeza de que as solicitações de e-mail válidas serão enviadas e os problemas registrados. Se, por algum motivo, seu coordenador puder criar e-mails com problemas, isso se tornará aparente muito rapidamente em uso e você terá todos os dados necessários dos logs para corrigir o problema. Por favor, esteja relaxado sobre isso; não há como criar testes que não garantam falhas. Se contentar com testes que testam um contrato de componentes e que mostram que a classe sempre se comporta de maneira responsável tanto quando uma solicitação respeita o contrato como quando ele quebra o contrato.

É importante que os componentes sejam testáveis isoladamente. Se testar um componente requer que os outros estejam totalmente presentes, então eles realmente não são componentes separados e você corre o risco de criar uma grande bola de lama. Na verdade, é mais perigoso do que zombar, porque você arrisca a criação de dependências ocultas nos detalhes de implementação desses outros componentes. Isso também significa que quaisquer alterações internas nesses componentes forçam uma atualização no processo de criação e teste para este. Isso escala muito e desperdiça o tempo de todos.

Não importe qualquer lógica da classe de email para a simulação. Por favor faça teste para sucesso e falha da classe mail, mas faça aqueles eventos arbitrários, não algo usando lógica copiada da classe real para a sua simulação. É uma perda de tempo e reintroduz o risco de dependências ocultas nos detalhes da implementação.

Construa seu sistema com base nesses princípios. Confie no sistema. Não o complique em uma bagunça porque você quer provar em cada parte que todo o sistema funciona. Isso falhará e tornará o fracasso mais provável.

tl; dr

A lei de Demeter aplica-se também aos testes.

    
por 29.01.2015 / 14:10
fonte
3

Eu não acho que há mais nada que você possa fazer além de testar se as funções dentro desse método são chamadas.

Como o método "orquestração" não contém muita lógica, o teste unitário não é tão importante. É um método que apenas liga outros métodos. Integra outros componentes. Portanto, deve haver testes de integração que cubram essas linhas de código. Testes unitários podem ser omitidos. Então eu não me preocuparia muito com isso desde que você tenha esses testes de integração. No entanto, como isso pode ser testado pela integração é outro tópico, porque você não deve ridicularizar todos os outros componentes e, portanto, obter algumas dependências com as quais terá que lidar.

    
por 29.01.2015 / 10:03
fonte
2

Você geralmente não precisa testar as classes do controlador, pois elas não devem conter lógica complexa. O trabalho real deve ser feito em outras classes, se estiver escrito corretamente, então, no final, você está essencialmente garantindo que o trabalho seja delegado corretamente.

A complexidade disso depende inteiramente da linguagem que você está usando, pois para fazer isso corretamente, você precisaria criar stubs. Idiomas como o Java podem ter dificuldades com isso, mas existem bibliotecas que o ajudam com isso, como o Mockito .

Se você precisar testar sua classe de controlador, então você deve idealmente remover as classes vizinhas e colocar classes de stub em seus lugares, então você simplesmente verificará que o método apropriado é chamado em resposta a cada entrada para sua classe de controlador. Eu lembro a você que todo o objetivo do teste de unidade é verificar se cada método está fazendo seu trabalho corretamente. Para uma classe de controlador, isso geralmente significa delegar tarefas adequadamente.

A alternativa ao uso de stubs seria realizar o trabalho real, embora isso possa ser facilmente sobrecarregado para testar, dada a complexidade e outras complicações, como escrever no banco de dados, então eu aconselharia contra ele.

Além disso, eu recomendo strongmente que você extraia apenas as informações necessárias nas classes do controlador da web e as passe para os métodos que fazem a lógica. A razão para isso é que você não precisa simular objetos de solicitação e resposta http, mas apenas o que você passa, e você não deveria ter que testar um método que recupera os dados necessários e passa-os para outro método (se passa um valor nulo, tudo bem, não teste mais do que você precisa).

Espero que ajude!

    
por 29.01.2015 / 10:04
fonte