Estou trabalhando com o seguinte sistema:
Network Data Feed -> Third Party Nio Library -> My Objects via adapter pattern
Recentemente, tivemos um problema em que atualizei a versão da biblioteca que estava usando, o que, entre outras coisas, causou que os carimbos de data / hora (retornados pela biblioteca de terceiros long
) fossem alterados de milissegundos após a época para nanossegundos após a época.
O problema:
Se eu escrever testes que zombam dos objetos da biblioteca de terceiros, meu teste estará errado se eu cometer um erro sobre os objetos da biblioteca de terceiros. Por exemplo, eu não percebi que os carimbos de data e hora alteravam a precisão, o que resultava em uma necessidade de alteração no teste de unidade, porque minha simulação retornou os dados incorretos. Isso não é um bug na biblioteca , aconteceu porque eu perdi algo na documentação.
O problema é que não posso ter certeza sobre os dados contidos nessas estruturas de dados porque não consigo gerar dados reais sem um feed de dados real. Esses objetos são grandes e complicados e possuem muitos dados diferentes. A documentação para a biblioteca de terceiros é ruim.
A questão:
Como posso configurar meus testes para testar esse comportamento? Não tenho certeza de que posso resolver esse problema em um teste de unidade, porque o teste em si pode facilmente estar errado. Além disso, o sistema integrado é grande e complicado e é fácil perder alguma coisa. Por exemplo, na situação acima, eu havia ajustado corretamente o tratamento do carimbo de data e hora em vários lugares, mas perdi um deles. O sistema parecia estar fazendo principalmente as coisas certas no meu teste de integração, mas quando o implantei na produção (que tem muito mais dados), o problema se tornou óbvio.
Não tenho um processo para meus testes de integração no momento. O teste é essencialmente: tente manter os testes de unidade bons, adicione mais testes quando as coisas quebrarem, implante no meu servidor de teste e verifique se as coisas parecem saudáveis, depois implante na produção. Esse problema de carimbo de data e hora passou nos testes de unidade porque as simulações foram criadas incorretamente; em seguida, passou no teste de integração porque não causou problemas imediatos e óbvios. Eu não tenho um departamento de controle de qualidade.
Timestamp
classe (contendo qualquer representação que eles querem) e fornecer métodos chamados (.seconds()
,.milliseconds()
,.microseconds()
,.nanoseconds()
) e de construtores curso nomeados. Então não haveria problemas.Respostas:
Parece que você já está fazendo a devida diligência. Mas ...
No nível mais prático, sempre inclua uma boa quantidade de testes de integração "de loop completo" no seu conjunto para seu próprio código e escreva mais asserções do que você pensa que precisa. Em particular, você deve ter um punhado de testes que executam um ciclo completo de criação, leitura e doação de validação.
E parece que você já está fazendo esse tipo de coisa. Você está apenas lidando com uma biblioteca esquisita e / ou complicada. E, nesse caso, é bom lançar alguns tipos de testes "é assim que a biblioteca funciona", que verificam sua compreensão da biblioteca e servem como exemplos de como usá-la.
Suponha que você precise entender e depender de como um analisador JSON interpreta cada "tipo" em uma sequência JSON. É útil e trivial incluir algo como isso em sua suíte:
Mas, em segundo lugar, lembre-se de que testes automatizados de qualquer tipo e com quase qualquer nível de rigor ainda não conseguirão protegê-lo contra todos os erros. É perfeitamente comum adicionar testes à medida que você descobre problemas. Não ter um departamento de controle de qualidade, significa que muitos desses problemas serão descobertos pelos usuários finais.
E em um grau significativo, isso é normal.
E em terceiro lugar, quando uma biblioteca altera o significado de um valor ou campo de retorno sem renomear o campo ou método ou "quebrar" o código dependente (talvez alterando seu tipo), eu ficaria muito infeliz com esse editor. E eu argumentaria que, mesmo que você devesse ter lido o changelog, se houver, provavelmente também deveria passar um pouco do seu estresse para o editor. Eu diria que eles precisam da crítica esperançosamente construtiva ...
fonte
(new JSONParser()).parse(datastream)
, pois eles capturam os dados diretamente deNetworkInterface
e todas as classes que realizam a análise real são privadas e programadas.NetworkInterface
... é algo em que você pode alimentar dados conectando a interface a uma porta no host local ou algo assim?NetworkInterface
. É um objeto de baixo nível para trabalhar diretamente com uma placa de rede e abrindo soquetes nele, etc.Resposta curta: é difícil. Você provavelmente está sentindo que não há boas respostas, e é porque não há respostas fáceis.
Resposta longa: como o @ptyx diz , você precisa de testes do sistema e testes de integração, além de testes de unidade:
Algumas sugestões específicas:
Vi a programação descrita como a atividade de aprender sobre um problema e um espaço de solução. Obter tudo perfeito antes do tempo pode não ser possível, mas você pode aprender depois disso. ("Corrigi o tratamento do registro de data e hora em vários lugares, mas perdi um. Posso alterar meus tipos de dados ou classes para tornar o tratamento de registro de data e hora mais explícito e difícil de perder, ou para torná-lo mais centralizado, para que eu tenha apenas um lugar para alterar? Posso modificar meus testes para verificar mais aspectos da manipulação do carimbo de data / hora? Posso simplificar meu ambiente de teste para facilitar isso no futuro? Posso imaginar alguma ferramenta que tornaria isso mais fácil? Em caso afirmativo, posso encontrar essa ferramenta no Google? "Etc.)
fonte
Eu discordo totalmente de você aqui. É um bug na biblioteca , um pouco insidioso na verdade. Eles alteraram o tipo semântico do valor de retorno, mas não o tipo programático do valor de retorno. Isso pode causar todos os tipos de estragos, especialmente se esse for um problema de versão menor, mas também se for um problema de versão principal.
Digamos que a biblioteca retornou um tipo de
MillisecondsSinceEpoch
, um invólucro simples que contém along
. Quando eles o alteravam para umNanosecondsSinceEpoch
valor, seu código não seria compilado e obviamente o indicaria os locais onde você precisa fazer alterações. A alteração não pôde corromper silenciosamente seu programa.Melhor ainda, seria um
TimeSinceEpoch
objeto que poderia adaptar sua interface à medida que mais precisão fosse adicionada, como adicionar um#toLongNanoseconds
método ao lado do#toLongMilliseconds
método, sem exigir nenhuma alteração no seu código.O próximo problema é que você não possui um conjunto confiável de testes de integração na biblioteca. Você deveria escrever isso. Melhor seria criar uma interface em torno dessa biblioteca para encapsulá-la para o resto do seu aplicativo. Várias outras respostas abordam isso (e outras continuam aparecendo enquanto digito). Os testes de integração devem ser executados com menos frequência do que os testes de unidade. É por isso que ter uma camada de buffer ajuda. Separe seus testes de integração em uma área separada (ou nomeie-os de maneira diferente) para poder executá-los conforme necessário, mas não sempre que executar seu teste de unidade.
fonte
...Ex()
métodos no Win32API). Se isso não for viável, "quebrar" o contrato renomeando a função (ou seu tipo de retorno) teria sido melhor do que alterar o comportamento.Você precisa de testes de integração e sistema.
Os testes de unidade são ótimos para verificar se seu código se comporta conforme o esperado. Como você percebe, não faz nada para desafiar suas suposições ou garantir que suas expectativas sejam saudáveis.
A menos que seu produto tenha pouca interação com sistemas externos ou interaja com sistemas tão conhecidos, estáveis e documentados, eles podem ser ridicularizados com confiança (isso raramente acontece no mundo real) - testes de unidade não são suficientes.
Quanto maior o nível de seus testes, mais eles o protegerão contra o inesperado. Isso tem um custo (conveniência, velocidade, fragilidade ...), portanto, os testes de unidade devem permanecer a base dos seus testes, mas você precisa de outras camadas, incluindo - eventualmente - um pouquinho de testes em humanos que ajudam bastante na captura coisas estúpidas que ninguém pensou.
fonte
O melhor seria criar um protótipo mínimo e entender como a biblioteca funciona exatamente. Ao fazer isso, você obterá algum conhecimento sobre a biblioteca com documentação insuficiente. Um protótipo pode ser um programa minimalista que usa essa biblioteca e executa a funcionalidade.
Caso contrário, não faz sentido escrever testes de unidade, com requisitos semi-definidos e fraca compreensão do sistema.
Quanto ao seu problema específico - sobre o uso de métricas incorretas: eu trataria isso como uma alteração nos requisitos. Depois de reconhecer o problema, altere os testes de unidade e o código.
fonte
Se você estivesse usando uma biblioteca popular e estável, talvez pudesse supor que ela não trará truques desagradáveis para você. Mas se coisas como o que você descreveu acontecerem com esta biblioteca, então obviamente não será uma. Após essa experiência ruim, sempre que algo der errado em sua interação com esta biblioteca, você precisará examinar não apenas a possibilidade de ter cometido um erro, mas também a possibilidade de que a biblioteca tenha cometido um erro. Então, digamos que esta é uma biblioteca sobre a qual você não tenha certeza.
Uma das técnicas empregadas com as bibliotecas sobre as quais não temos certeza é criar uma camada intermediária entre o nosso sistema e as referidas bibliotecas, que abstraem a funcionalidade oferecida pelas bibliotecas, afirma que nossas expectativas em relação à biblioteca são corretas e também simplifica bastante nossa vida no futuro, devemos decidir dar a inicialização a essa biblioteca e substituí-la por outra biblioteca que se comporte melhor.
fonte
assert
palavra-chave (ou função ou recurso, dependendo do idioma que você está usando) é sua amiga. Não estou falando de asserções nos testes de unidade / integração, estou dizendo que a camada de isolamento deve ser muito pesada com asserções, afirmando tudo o que é assertável sobre o comportamento da biblioteca.