Eu testei a minha classe por unidade, agora como começar com um teste de integração?

19

Eu escrevi uma classe que gerencia destinatários em uma lista do MailChimp, chamada MailChimpRecipient. Ele usa a classe MCAPI, que é um wrapper de API de terceiros.

http://apidocs.mailchimp.com/api/1.3/ http://apidocs.mailchimp.com/api/downloads/

Como passo o objeto MCAPI no construtor do objeto MailChimpRecipient, escrevi testes de unidade usando o PHPUnit que testam toda a lógica da minha própria classe (não estou testando a classe MCAPI). Tenho 100% de cobertura de código e todos os testes são aprovados. Isso é feito zombando e stubbing do objeto MCAPI.

Meu próximo passo foi escrever um teste de integração, também usando o PHPUnit, onde eu construía o dispositivo MailChimpRecipient usando um objeto MCAPI real, configurado para usar uma lista real do MailChimp.

Escrevi o que considero um teste de integração, que basicamente executa testes contra a interface pública do objeto, como:

public function testAddedRecipientCanBeFound()
{
    $emailAddress = '[email protected]';
    $forename = 'Fred';
    $surname = 'Smith';

    // First, delete the email address if it is already on the list
    $oldRecipient = $this->createRecipient();
    if($oldRecipient->find($emailAddress))
    {
        $oldRecipient->delete();
    }
    unset($oldRecipient);

    // Add the recipient using the test data
    $newRecipient = $this->createRecipient();
    $newRecipient->setForename($forename);
    $newRecipient->setSurname($surname);
    $newRecipient->setEmailAddress($emailAddress);
    $newRecipient->add();
    unset($newRecipient);

    // Assert that the recipient can be found using the same email address
    $this->assertTrue($this->_recipient->find($emailAddress));
}

O teste de "integração" não testa nenhum elemento interno da classe - apenas garante que, dado um objeto MCAPI real, ele se comporte como anunciado.

Isso está correto? Essa é a melhor maneira de executar um teste de intergação? Afinal, os internos foram testados com um teste de unidade. Estou certo ao pensar que o teste de integração existe para testar se ele realmente funciona, de acordo com a forma como seu comportamento é anunciado?

Para dar um passo adiante, a classe MailChimpRecipient implementa uma interface, que também será implementada por outras classes. A idéia é usar uma fábrica para passar diferentes tipos de objetos de destinatários da lista de endereçamento para o meu código, que fazem a mesma coisa, embora usando diferentes fornecedores de lista de endereçamento. Como meus testes de integração testam a interface, que tal usá-la para todas as classes que implementam a interface? Então, no futuro, se eu criar uma nova classe para ser usada de forma intercambiável, eu posso executar o mesmo teste de integração antes de inseri-lo em um projeto.

Isso soa razoável? Os testes de unidade testam as partes internas de um objeto, os testes de integração garantem que ele se comporte como anunciado?

Lewis Bassett
fonte
4
Eu acho que você tem muita lógica em seu teste. Você executa muito código até fazer a afirmação. Você provavelmente deseja testar a exclusão de um destinatário primeiro. Mas isso não está respondendo à sua pergunta, apenas um comentário.
hakre
1
Bem, você deve usar a setUpfunção para estabelecer os motivos para executar seus testes. Se a entrada for indefinida, bem, você realmente não pode testar. A entrada precisa ser precisa, rigorosa e sempre a mesma. Se uma pré-condição de um teste não for atendida, pule o teste. Em seguida, analise por que ele ignora e se você precisa adicionar testes adicionais e / ou setUpse não foi feito corretamente.
hakre
1
Também não codifique os valores de teste dentro de um teste próprio provavelmente, mas faça com que os membros da classe possam ser compartilhados entre testes (e alterados em um local central) ou usados DataProvider(essa é uma função que oferece entrada como parâmetros para um teste).
hakre
1
Digite o significado de tudo o que sua função de teste opera. Ao testar a adição de um destinatário e desejar garantir que ele ainda não exista, você deve pelo menos afirmar a exclusão, caso ela entre em ação. Caso contrário, a pré-condição do seu teste não será testada.
hakre
1
+1 em boa pergunta, mas também votou na migração para programadores. Parece que é aí que as perguntas sobre estratégias de teste pertencem
GordonM

Respostas:

17

Ao testar seu código, você deve prestar atenção em três áreas:

  • Teste de cenário
  • Teste funcional
  • Teste de unidade

Normalmente, a quantidade de teste que você tem em cada categoria teria o formato de uma pirâmide, significando muitos testes de unidade na parte inferior, alguns testes funcionais no meio e apenas alguns testes de cenário.

Com um teste de unidade, você zomba de tudo o que a classe em teste usa e você a examina em puro isolamento (é por isso que é importante garantir que, dentro da sua classe, você recupere todas as dependências através da injeção para que possam ser substituídas em teste).

Com o teste de unidade, você testa todas as possibilidades, não apenas o 'caminho feliz', mas também todas as condições de erro.

Se você tiver certeza absoluta de que todas as suas unidades funcionam isoladamente, escreva alguns testes (testes funcionais) para garantir que as unidades também funcionem quando combinadas. Em seguida, você escreve um teste de cenário, que testa a fiação entre todos os módulos funcionais.

Por exemplo, suponha que você esteja testando um carro.

Você poderia montar o carro inteiro e, como motorista, verificar todas as condições possíveis, mas isso seria realmente difícil de fazer.

Em vez disso, você testaria uma pequena parte do mecanismo com todas as possibilidades (teste de unidade)

Então você testa o motor inteiro (separado do carro), o que seria um teste funcional.

Como último teste, você coloca a chave, liga o carro e o leva até o estacionamento. Se estiver funcionando, você saberá que todas as peças (bateria, combustível, motor, etc.) estão conectadas e, desde que você as testou isoladamente, pode ter certeza de que todo o carro está funcionando corretamente.

Portanto, no seu caso, você testou todas as condições de erro e o caminho feliz em seu teste de unidade e sabe que só precisa fazer um teste de ponta a ponta com os 'componentes reais' para verificar se a fiação está correta.

Alguns outros pontos,

  • Evite lógica condicional no seu teste de unidade. Se você precisar se limpar, o uso de algum tipo de estado global e os testes poderão influenciar-se repentinamente.
  • Não especifique nenhum dado que não seja relevante para o teste. Se eu mudasse o nome ou sobrenome, o teste falharia? Provavelmente não porque o endereço de e-mail seja importante, mas porque você o mencionou explicitamente em seu teste, não tenho certeza. Tente examinar o Padrão do Construtor para construir seus dados de teste e explicitar o que é realmente importante.
Wouter de Kort
fonte
Obrigado, isso confirma muito do que eu pensava. Apenas para esclarecer - este NÃO é um teste de unidade. Eu já escrevi um teste de unidade, que testa o objeto em isolamento completo e tem 100% de cobertura de código do objeto. Era para ser um teste de integração, para garantir que funcione quando eu injetar um objeto MCAPI real nele. Eu só preciso excluir todos os destinatários adicionados à lista - isso é tudo a limpeza e implementada para garantir que nenhum dos testes influencie um ao outro. O que você sugeriria?
1
sim! Eu entendi que você já fez os testes de unidade. O objeto MCAPI controla os destinatários e é essa a limpeza que você deve fazer? Se for o 'problema' de terceiros, não há nada que você possa fazer em um teste de integração. Se você, por outro lado, acompanha a lista, evite os dados globais (e os singletons) para garantir que os testes não se influenciem. Em um mundo perfeito, limpar as coisas quando um teste começa / termina, aponta para uma falha de design, mas no mundo real, você nem sempre pode evitá-la.
Wouter de Kort
1
Eu acrescentaria que o teste de cenário provavelmente não é realmente algo para o qual o PHPUnit é adequado. Você pode querer procurar alguma ferramenta que você possa executar em um navegador como o Selenium, ou uma ferramenta que possa simular um navegador, como o jMeter.
precisa saber é o seguinte
Obrigado rapazes! Com certeza há muito a aprender quando se trata de escrever um bom código testável, não existe. Encomendei uma cópia deste livro: amazon.co.uk/… . Felizmente, o que todos vocês disseram fará mais sentido depois de ler isso. @ Wouter, estou apenas excluindo um destinatário porque o teste faria com que um endereço de email fosse adicionado à lista. Estou excluindo para que a lista não seja afetada por esse teste.
1
@LewisBassett Não sou desenvolvedor de Php, mas os padrões de teste do xUnit ( amazon.com/xUnit-Test-Patterns-Refactoring-Code/dp/0131495054 ) são definitivamente uma boa leitura. Também os artigos em misko.hevery.com/code-reviewers-guide são realmente interessantes.
Wouter de Kort