Como você escreve testes para código que depende de implementações externas concretas que não podem ser ridicularizadas?

17

Antecedentes: Estou pensando em tentar introduzir o conceito de testes de unidade para meus colegas de trabalho, criando alguns para um módulo em que estou trabalhando; os requisitos dele foram alterados recentemente e exigem mais abstrações / interações, portanto, parece uma boa maneira de desenvolver um conjunto de testes que "provarão" que ele funciona sem ter que procurar manualmente pelo aplicativo.

A questão, no entanto, é que o módulo depende de fatores externos imbatíveis, como PDFs e XSL. Basicamente, leio XML do banco de dados e aplico uma transformação XSL a ele, depois o converto em PDF usando uma biblioteca chamada ABCPDF. Este PDF é mesclado com outro PDF com base em um modelo estático. Sei que posso testar o XML e garantir que os valores estejam corretos, mas muitos dos possíveis bugs e problemas estão relacionados à exibição real do documento finalizado - por exemplo, minúcias como o comprimento das seqüências de texto, onde determinadas áreas HTML estão localizado em relação ao documento, etc. É possível testar essas coisas (eu percebo que esses provavelmente são testes de integração ou .. o terceiro tipo de teste cujo nome eu esqueço [não são os testes de aceitação, o outro tipo] e não a unidade testes), já que eu não sei como zombar de um PDF facilmente, criando-o e depois lendo-o novamente ou criando uma string HTML (ou seja, XML transformado) e analisando-o manualmente para verificar a presença de determinadas células da tabela em relação a outras células da tabela.

Em uma situação como essa, devo me concentrar apenas nos testes de unidade para garantir que as informações estejam corretas e que eu possa criar o PDF, mesclá-los ou qualquer outra coisa e recorrer a testes manuais para os problemas reais de exibição?

Wayne Molina
fonte
6
"fatores externos imbatíveis" é uma dica de que você não está fazendo testes de unidade em primeiro lugar. Isso significa teste de integração . Qual você quer fazer? Teste de unidade do seu código ou teste de integração dessa coisa composta ? Por favor, escolha um ou outro, porque é difícil falar sobre os dois ao mesmo tempo.
S.Lott 20/05
2
Não estou comprando "imbatível". Vou aceitar "que não sei como zombar", o que significa apenas que sua verdadeira pergunta é "como zombar?".
Rein Henrichs
Provavelmente :) Estou familiarizado com a imitação do XML usado, mas não com a imitação de um PDF ou documento HTML real, onde a formatação é importante.
Wayne Molina
1
Eu acho que você quer dizer "funcional" (fim aplicação ao fim) ou "sistema" (múltiplas aplicações de ponta a ponta) Testes
Gary Rowe
@ Gary - Sim, funcional era a palavra. Lembro-me deles agora de aprender Rails: modelos de testes de unidade, controladores de testes funcionais, integração testa tudo.
Wayne Molina

Respostas:

13

Teste o recurso e não a unidade

Usando entradas xml conhecidas, produza um PDF e verifique manualmente (e meticulosamente) se está correto. Em seguida, salve-o como uma referência.

Testes futuros usando as mesmas entradas xml podem fazer uma comparação de arquivo binário com a referência.

Se uma comparação no nível do arquivo for insatisfatória, exiba o PDF no final do teste e faça capturas de tela, e compare o teste automatizado com as capturas de tela de referência.

Steven A. Lowe
fonte
+1 porque você está interessado apenas no resultado final neste nível. Se você alterar a implementação de como chegar ao PDF, não precisará alterar seu teste funcional.
Gary Rowe
2
+1 para obter bons conselhos, é isso que fazemos em nosso projeto atual. Criamos um conjunto de ferramentas personalizado para fazer a comparação do PDF, o que nos permite omitir as peças alteradas nos documentos, como carimbos de data e hora. Advertência: mudar para um renderizador de PDF diferente (versão do) pode causar mudanças sutis no layout, causando comparação binária direta com montes de sinais de falsos positivos.
Péter Török
5

Normalmente, em um caso como esse, você abstrai tudo o que não pode testar atrás de uma implementação que pode usar com uma interface. Vou fazer algo bobo como o PDF Builder, porque isso parece razoável.

public class PdfBuilder : IPdfBuilder
{
  public byte[] BuildPdf(...)
  {
    // actual untestable code here
  }
}

public interface IPdfBuilder
{
  byte[] BuildPdf(...);
}

Você pode simular o IPdfBuilder em seus testes para fazer o que quiser. Isso geralmente significa que você precisa começar a usar um contêiner de IoC ( /programming/871405/why-do-i-need-an-ioc-container-as-opposed-to-straightforward-di-code e /programming/21288/which-net-dependency-injection-frameworks-are-worth-looking-into como ponto de partida) se você não estiver usando um agora.

E testes que não são testes de unidade são freqüentemente chamados de testes de integração. Os testes de integração complicados geralmente não valem totalmente a pena; portanto, você apenas abstrai essa parte e reduz a quantidade de lógica de negócios nessa abstração para poder testá-la em um teste de unidade.

Deixe-me saber se isso não está totalmente claro.

Travis
fonte
+1 para ocultar o código não testável. Em seguida, você pode fazer testes manuais até descobrir o que precisa atravessar essa interface para obter o resultado certo e testar a unidade para que isso seja gerado corretamente para obter os testes de unidade de regressão.
Ethel Evans
1

Eu construí algo muito parecido há algum tempo e apenas usei testes visuais básicos. O teste não precisa ser automatizado; portanto, não há nada errado em apenas procurar um resultado esperado (obviamente, em uma variedade de situações predeterminadas). Muitas vezes, uma imagem vale mais que mil testes no que diz respeito ao visual . Uso extensivamente o teste de unidade automatizado, mas acho que algumas pessoas podem se deixar levar um pouco ao fazer o teste da GUI ou qualquer IMHO visual. Com determinado produto, reconheço que essa abordagem "suficientemente boa" não será suficiente, portanto, YMMV.

Eu ficaria um pouco preocupado, no entanto, com as externalidades imbatíveis. Isso pode ser um sinal de acoplamento rígido, o que é bom evitar como regra geral, mas não vou especular muito sobre seu código a esse respeito sem mais detalhes.

Morgan Herlocker
fonte
É muito acoplado, mas é uma área que não posso consertar, já que não há comprometimento para torná-lo pouco acoplado e não há recursos dedicados à refatoração (mas esse é um conjunto totalmente diferente de problemas).
Wayne Molina