Como você detecta problemas de dependência com testes de unidade quando usa objetos simulados?

98

Você tem uma classe X e escreve alguns testes de unidade que verificam o comportamento X1. Há também a classe A, que toma X como uma dependência.

Ao escrever testes de unidade para A, você zomba de X. Em outras palavras, durante o teste de unidade A, você define (postula) que o comportamento da zombaria de X é X1. O tempo passa, as pessoas usam seu sistema, precisam mudar, X evolui: você modifica X para mostrar o comportamento X2. Obviamente, os testes de unidade do X falharão e você precisará adaptá-los.

Mas o que com A? Os testes de unidade para A não falharão quando o comportamento de X for modificado (devido à zombaria de X). Como detectar que o resultado de A será diferente quando executado com o X "real" (modificado)?

Estou esperando respostas ao longo da linha de: "Esse não é o objetivo do teste de unidade", mas que valor tem então o teste de unidade? Isso realmente apenas lhe diz que, quando todos os testes passam, você não introduziu uma mudança de quebra? E quando o comportamento de alguma classe muda (de boa ou má vontade), como você pode detectar (de preferência de forma automatizada) todas as consequências? Não devemos nos concentrar mais nos testes de integração?

bvgheluwe
fonte
36
Além de todas as respostas sugeridas, devo dizer que discordo da seguinte declaração: 'Realmente diz a você que, quando todos os testes passam, você não introduziu uma mudança de quebra?' Se você realmente acha que a remoção do medo de refatoração é de pouco valor, você está no caminho rápido para a escrita de código insustentável
crizzis
5
O teste de unidade informa se a sua unidade de código se comporta conforme o esperado. Não mais ou menos. Zombarias e duplas de teste fornecem um ambiente artificial controlado para você exercitar sua unidade de código (isoladamente) para ver se ela atende às suas expectativas. Não mais ou menos.
Robert Harvey
2
Acredito que sua premissa está incorreta. Quando você menciona, X1está dizendo que Ximplementa interface X1. Se você alterar a interface X1para X2a simulação que você usou nos outros testes não deverá mais compilar, portanto, você será forçado a corrigir esses testes também. Mudanças no comportamento da classe não devem importar. De fato, sua classe Anão deve depender dos detalhes da implementação (que é o que você mudaria nesse caso). Portanto, os testes de unidade Aainda estão corretos e eles informam que Afunciona, dada a implementação ideal da interface.
Bakuriu 27/03
5
Não sei você, mas quando tenho que trabalhar em uma base de código sem testes, estou morrendo de medo de quebrar alguma coisa. E porque? Porque isso acontece com tanta frequência que algo quebra quando não se destina. E abençoe os corações dos nossos testadores, eles não podem testar tudo. Ou até perto. Porém, um teste de unidade será feliz com a rotina chata após a rotina chata.
corsiKa

Respostas:

125

Ao escrever testes de unidade para A, você zomba de X

Você? Não, a menos que seja absolutamente necessário. Eu tenho que se:

  1. X é lento ou
  2. X tem efeitos colaterais

Se nenhuma dessas opções se aplicar, meus testes de unidade também Aserão testados X. Fazer qualquer outra coisa seria levar os testes de isolamento a um extremo ilógico.

Se você tem partes do seu código usando simulações de outras partes, então eu concordo: qual é o sentido desses testes de unidade? Então não faça isso. Deixe esses testes usarem as dependências reais, pois eles formam testes muito mais valiosos dessa maneira.

E se algumas pessoas ficam chateadas com você chamando esses testes de "testes unitários", basta chamá-los de "testes automatizados" e continuar escrevendo bons testes automatizados.

David Arno
fonte
94
@ Laiv, Não, os testes de unidade devem atuar como uma unidade, ou seja, executados isoladamente, a partir de outros testes . Nós e gráficos podem fazer uma caminhada. Se eu puder executar um teste de ponta a ponta livre e isolado de efeitos colaterais em um curto espaço de tempo, é um teste de unidade. Se você não gostar dessa definição, chame-a de teste automatizado e pare de escrever testes de porcaria para se encaixar na semântica boba.
David Arno
9
@DavidArno Existe, infelizmente, uma definição muito ampla de isolado. Alguns gostariam que uma "unidade" incluísse a camada intermediária e o banco de dados. Eles podem acreditar no que quiserem, mas as chances são de que, em um desenvolvimento de qualquer tamanho, as rodas se soltem em pouco tempo, à medida que o gerente de construção o jogar fora. Geralmente, se eles estão isolados na montagem (ou equivalente), tudo bem. Nota: se você codificar para interfaces, é muito mais fácil adicionar zombaria e DI mais tarde.
Robbie Dee
13
Você está defendendo um tipo diferente de teste, em vez de responder à pergunta. Qual é um ponto válido, mas essa é uma maneira bastante discreta de fazer isso.
Phil Frost
17
@PhilFrost, para citar a mim mesmo: " E se algumas pessoas ficam chateadas com você chamando esses testes," testes de unidade ", basta chamá-los de" testes automatizados "e continuar escrevendo bons testes automatizados. " Faça testes úteis, não testes tolos que simplesmente atendem a alguma definição aleatória de uma palavra. Ou então, aceite que talvez você tenha sua definição de "teste de unidade" errada e que esteja usando zombarias demais porque está errada. De qualquer forma, você terá melhores testes.
David Arno
30
Estou com @DavidArno neste; minha estratégia de teste mudou depois de assistir a essa conversa de Ian Cooper: vimeo.com/68375232 . Para resumir em uma única frase: não teste as aulas . Testar comportamentos . Seus testes não devem ter conhecimento das classes / métodos internos usados ​​para implementar o comportamento desejado; eles devem conhecer apenas a superfície pública da sua API / biblioteca e devem testar isso. Se os testes têm muito conhecimento, você está testando os detalhes da implementação e seus testes se tornam quebradiços, acoplados à sua implementação e, na verdade, apenas uma âncora no seu pescoço.
Richiban 28/03
79

Você precisa dos dois. Testes de unidade para verificar o comportamento de cada uma de suas unidades e alguns testes de integração para garantir que eles estejam conectados corretamente. O problema de depender apenas de testes de integração é a explosão combinatória resultante de interações entre todas as suas unidades.

Digamos que você tenha a classe A, que exige 10 testes de unidade para cobrir totalmente todos os caminhos. Então você tem outra classe B, que também exige 10 testes de unidade para cobrir todos os caminhos que o código pode percorrer. Agora, digamos que em seu aplicativo, você precisa alimentar a saída de A em B. Agora, seu código pode seguir 100 caminhos diferentes, da entrada de A à saída de B.

Com os testes de unidade, você só precisa de 20 testes de unidade + 1 teste de integração para cobrir completamente todos os casos.

Com os testes de integração, você precisará de 100 testes para cobrir todos os caminhos de código.

Aqui está um vídeo muito bom sobre as desvantagens de confiar apenas nos testes de integração. JB Rainsberger Integrated Tests Are A Scam HD

Eternal21
fonte
1
Tenho certeza de que não é coincidência que o ponto de interrogação sobre a eficácia dos testes de integração tenha andado de mãos dadas com os testes de unidade que cobrem cada camada.
Robbie Dee
16
Certo, mas em nenhum lugar seus 20 testes de unidade exigem zombaria. Se você tem seus 10 testes de A que abrangem todos os A e seus 10 testes que abrangem todos os B e também retestar 25% de A como um bônus, isso parece entre "bom" e uma coisa boa. Zombando A em testes Bs parece ativamente estúpido (a menos que há realmente uma razão pela qual A é um problema, por exemplo, é o banco de dados ou é a que traz uma longa teia de outras coisas)
Richard Tingle
9
Não concordo com a idéia de que um único teste de integração seja suficiente se você desejar uma cobertura completa. As reações de B às saídas A variam de acordo com a saída; se a alteração de um parâmetro em A alterar sua saída, B poderá não tratá-lo corretamente.
Matthieu M. 28/03
3
@ Eternal21: Meu argumento é que, às vezes, o problema não está no comportamento individual, mas em uma interação inesperada. Ou seja, quando a cola entre A e B se comporta inesperadamente em algum cenário. Assim, ambos act A e B de acordo com as especificações, e o caso feliz funciona, mas em algumas entradas há um bug no código de cola ...
Matthieu M.
1
@MatthieuM. Eu diria que isso está além do escopo do teste de unidade. O código da cola pode ser testado por unidade, enquanto as interações entre A e B através do código da cola são um teste de integração. Quando casos específicos ou erros de borda são encontrados, eles podem ser adicionados aos testes de unidade de código de cola e, finalmente, verificados nos testes de integração.
Andrew T Finnell
72

Ao escrever testes de unidade para A, você zomba de X. Em outras palavras, durante o teste de unidade A, você define (postula) que o comportamento da zombaria de X é X1. O tempo passa, as pessoas usam seu sistema, precisam mudar, X evolui: você modifica X para mostrar o comportamento X2. Obviamente, os testes de unidade do X falharão e você precisará adaptá-los.

Uau, espere um momento. As implicações dos testes para a falha do X são importantes demais para serem ignoradas dessa maneira.

Se alterar a implementação do X de X1 para X2 interromper os testes de unidade do X, isso indica que você fez uma alteração incompatível com o contrato X.

X2 não é um X, no sentido de Liskov , portanto, você deve pensar em outras maneiras de atender às necessidades de seus stakeholders (como a introdução de uma nova especificação Y, implementada pelo X2).

Para informações mais detalhadas, consulte Pieter Hinjens: o fim das versões de software ou Rich Hickey Simple Made Easy .

Da perspectiva de A, há uma condição prévia de que o colaborador respeite o contrato X. E sua observação é efetivamente que o teste isolado de A não oferece nenhuma garantia de que A reconheça colaboradores que violam o contrato X.

Rever testes integrados são uma farsa ; em alto nível, espera-se que você tenha quantos testes isolados necessários para garantir que X2 implemente o contrato X corretamente e quantos testes isolados necessários para garantir que A faça a coisa certa, com respostas interessantes de um X, e um número menor de testes integrados para garantir que X2 e A concordem com o que X significa.

Você verá algumas vezes essa distinção expressa como testes solitários versus sociabletestes; veja Jay Fields trabalhando efetivamente com testes de unidade .

Não devemos nos concentrar mais nos testes de integração?

Novamente, ver testes integrados é uma farsa - Rainsberger descreve em detalhes um ciclo de feedback positivo que é comum (em suas experiências) a projetos que contam com testes integrados (ortografia de nota). Em resumo, sem os testes isolados / solitários aplicando pressão no projeto , a qualidade diminui, levando a mais erros e testes mais integrados ....

Você também precisará de (alguns) testes de integração. Além da complexidade introduzida por vários módulos, a execução desses testes tende a ter mais resistência do que os testes isolados; é mais eficiente repetir verificações muito rápidas quando o trabalho está em andamento, salvando as verificações adicionais para quando você pensa que está "pronto".

VoiceOfUnreason
fonte
8
Essa deve ser a resposta aceita. A pergunta descreve uma situação em que o comportamento de uma classe foi modificado de maneira incompatível, mas ainda exteriormente parece idêntico. O problema aqui reside no design do aplicativo, não nos testes de unidade. A maneira como essa circunstância seria detectada no teste é usando um teste de integração entre essas duas classes.
Nick Coad
1
"sem os testes isolados / solitários aplicando pressão ao projeto, a qualidade diminui". Eu acho que esse é um ponto importante. Juntamente com as verificações de comportamento, os testes de unidade têm o efeito colateral para forçar você a ter um design mais modular.
MickaëlG 30/03
Suponho que tudo isso seja verdade, mas como isso me ajuda se uma dependência externa introduzir uma alteração incompatível com o contrato X? Talvez uma classe com desempenho de E / S em uma biblioteca quebre a compatibilidade, e estamos zombando do X porque não queremos que os testes de unidade no IC dependam de E / S pesada. Acho que o OP está pedindo para testar isso, e não entendo como isso responde à pergunta. Como testar isso?
gerrit 23/08
15

Deixe-me começar dizendo que a premissa básica da questão é falha.

Você nunca está testando (ou zombando) implementações, você está testando (e zombando) interfaces .

Se eu tiver uma classe X real que implemente a interface X1, posso escrever um XM falso que também esteja em conformidade com o X1. Então minha classe A deve usar algo que implemente X1, que pode ser classe X ou zombar de XM.

Agora, suponha que alteremos o X para implementar uma nova interface X2. Bem, obviamente meu código não compila mais. A requer algo que implementa X1 e que não existe mais. O problema foi identificado e pode ser corrigido.

Suponha que, em vez de substituir o X1, apenas o modifiquemos. Agora a classe A está pronta. No entanto, o XM falso não implementa mais a interface X1. O problema foi identificado e pode ser corrigido.


Toda a base para testes de unidade e zombaria é que você escreve um código que usa interfaces. Um consumidor de uma interface não se importa como o código é implementado, apenas com o qual o mesmo contrato é respeitado (entradas / saídas).

Isso ocorre quando seus métodos têm efeitos colaterais, mas acho que isso pode ser excluído com segurança porque "não pode ser testado ou ridicularizado".

Vlad274
fonte
11
Essa resposta faz muitas suposições que não precisam ser mantidas. Primeiramente, assume-se, grosso modo, que estamos em C # ou Java (ou, mais precisamente, que estamos em uma linguagem compilada, que a linguagem tem interfaces e que X implementa uma interface; nada disso precisa ser verdadeiro). Em segundo lugar, pressupõe que qualquer alteração no comportamento ou "contrato" de X requer uma alteração na interface (como entendida pelo compilador) que o X implementa. Isto é claramente não é verdade, mesmo que estão em Java ou C #; você pode alterar uma implementação de método sem alterar sua assinatura.
Mark Amery
6
@MarkAmery É verdade que a terminologia da "interface" é mais específica para C # ou Java, mas acho que o ponto permanece assumindo um "contrato" de comportamento definido (e se isso não for codificado, detectar automaticamente isso é impossível). Você também está completamente certo de que uma implementação pode ser alterada sem uma alteração no contrato. Mas uma mudança na implementação sem uma alteração na interface (ou contrato) não deve afetar nenhum consumidor. Se o comportamento de A depende de como a interface (ou contrato) é implementada, é impossível (significativamente) o teste de unidade.
Vlad274
1
"Você também está completamente certo de que uma implementação pode ser alterada sem uma alteração no contrato" - embora também seja verdade, esse não é o ponto que eu estava tentando enfatizar. Em vez disso, estou afirmando uma distinção entre o contrato (o entendimento de um programador sobre o que um objeto deve fazer, talvez especificado na documentação) e a interface (uma lista de assinaturas de método, entendida pelo compilador) e dizendo que o contrato pode ser alterado sem alterar a interface. Todas as funções com a mesma assinatura são intercambiáveis ​​da perspectiva do sistema de tipos, mas não na realidade!
precisa
4
@ MarkAmery: Não acho que Vlad esteja usando a palavra "interface" no mesmo sentido que você está usando; da maneira como leio a resposta, não se trata de interfaces no sentido estreito de C # / Java (isto é, um conjunto de assinaturas de métodos), mas no sentido geral da palavra, conforme usado, por exemplo, nos termos "interface de programação de aplicativos" ou mesmo " interface de usuário". [...]
Ilmari Karonen 29/03/18
6
@IlmariKaronen Se Vlad estiver usando "interface" para significar "contrato", e não no sentido estreito de C # / Java, a declaração "Agora, suponha que alteremos X para implementar uma nova interface X2. Bem, obviamente, meu código não compila mais. " é simplesmente falso, pois você pode alterar um contrato sem alterar nenhuma assinatura de método. Mas, honestamente, acho que o problema aqui é que Vlad não está usando nenhum dos significados de maneira consistente, mas os está confundindo - o que leva a afirmar que qualquer alteração no contrato X1 necessariamente causará um erro de compilação sem perceber que isso é falso .
Mark Amery
9

Tomando suas perguntas por sua vez:

qual o valor do teste de unidade

Eles são baratos para escrever e executar, e você obtém feedback antecipado. Se você quebrar o X, descobrirá mais ou menos imediatamente se tiver bons testes. Nem pense em escrever testes de integração, a menos que você tenha testado todas as suas camadas (sim, mesmo no banco de dados).

Isso realmente apenas diz a você que, quando todos os testes passam, você não introduziu uma alteração de última hora

Ter testes aprovados pode realmente dizer muito pouco. Você pode não ter escrito testes suficientes. Você pode não ter testado cenários suficientes. A cobertura de código pode ajudar aqui, mas não é uma bala de prata. Você pode ter testes que sempre passam. Daí o vermelho ser o primeiro passo muitas vezes esquecido do refatorar vermelho, verde.

E quando o comportamento de alguma classe muda (de boa ou má vontade), como você pode detectar (de preferência de forma automatizada) todas as consequências

Mais testes - embora as ferramentas estejam ficando cada vez melhores. MAS você deve definir o comportamento da classe em uma interface (veja abaixo). Nota: sempre haverá um local para testes manuais no topo da pirâmide de testes.

Não devemos nos concentrar mais nos testes de integração?

Cada vez mais testes de integração não são a resposta, eles são caros para escrever, executar e manter. Dependendo da configuração da sua compilação, seu gerente de compilação pode excluí-los de qualquer maneira, tornando-os dependentes da lembrança de um desenvolvedor (nunca é uma coisa boa!).

Vi desenvolvedores passarem horas tentando corrigir testes de integração quebrados que teriam encontrado em cinco minutos se tivessem bons testes de unidade. Caso contrário, tente executar o software - é com isso que seus usuários finais se preocupam. Não adianta ter um milhão de testes de unidade que passam se todo o castelo de cartas cai quando o usuário executa o conjunto inteiro.

Se você deseja garantir que a classe A consome a classe X da mesma maneira, você deve usar uma interface em vez de uma concretização. Em seguida, é provável que uma mudança de quebra seja detectada no momento da compilação.

Robbie Dee
fonte
9

Está correto.

Os testes de unidade existem para testar a funcionalidade isolada de uma unidade, uma verificação à primeira vista de que funciona da maneira pretendida e não contém erros estúpidos.

Os testes de unidade não existem para testar se o aplicativo inteiro funciona.

O que muita gente esquece é que os testes de unidade são apenas os meios mais rápidos e sujos de validar seu código. Depois de saber que suas pequenas rotinas funcionam, você também precisará executar os testes de integração. O teste de unidade por si só é marginalmente melhor do que nenhum teste.

A razão pela qual temos testes de unidade é que eles devem ser baratos. Rápido para criar, executar e manter. Depois de começar a transformá-los em min testes de integração, você estará em um mundo de dor. Você também pode fazer o teste de integração completo e ignorar completamente o teste de unidade, se quiser fazer isso.

agora, algumas pessoas pensam que uma unidade não é apenas uma função de uma classe, mas toda a classe em si (inclusive eu). No entanto, tudo isso faz é aumentar o tamanho da unidade, para que você possa precisar de menos testes de integração, mas ainda precisa. Ainda é impossível verificar se o seu programa faz o que deve fazer sem um conjunto completo de testes de integração.

e, em seguida, você ainda precisará executar o teste de integração completo em um sistema ativo (ou semi-ativo) para verificar se ele funciona com as condições que o cliente usa.

gbjbaanb
fonte
2

Os testes de unidade não comprovam a exatidão de nada. Isso é verdade para todos os testes. Normalmente, os testes de unidade são combinados com o design baseado em contrato (o design por contrato é outra maneira de dizer isso) e, possivelmente, provas automatizadas de correção, se a correção precisar ser verificada regularmente.

Se você possui contratos reais, consistindo em invariantes de classe, pré-condições e pós-condições, é possível provar a correção hierarquicamente, baseando a correção dos componentes de nível superior nos contratos dos componentes de nível inferior. Este é o conceito fundamental por trás do design por contrato.

Frank Hileman
fonte
Os testes de unidade não comprovam a exatidão de nada . Não tenho certeza de que entendi isso, os testes de unidade verificam seus próprios resultados com certeza? Ou você quis dizer que um comportamento não pode ser provado correto, pois isso pode abranger várias camadas?
Robbie Dee
7
@RobbieDee Eu acho que ele quis dizer que, quando você testa fac(5) == 120, você não provou que fac()realmente retorna o fatorial de seu argumento. Você só provou que fac()retorna o fatorial de cinco quando passa 5. E mesmo isso não é certo, como é fac()possível retornar 42nas primeiras segundas-feiras após um eclipse total em Timbuktu ... O problema aqui é que você não pode provar a conformidade verificando entradas de teste individuais, seria necessário verificar todas as entradas possíveis, e também prove que você não esqueceu nada (como ler o relógio do sistema).
cmaster
1
Os testes @RobbieDee (incluindo testes de unidade) são um substituto ruim, geralmente o melhor disponível, para o objetivo real, uma prova verificada por máquina. Considere todo o espaço de estado das unidades em teste, incluindo o espaço de estado de qualquer componente ou zombaria. A menos que você tenha um espaço de estado muito restrito, os testes não podem cobrir esse espaço de estado. A cobertura completa seria uma prova, mas isso só está disponível para pequenos espaços de estado, por exemplo, testando um único objeto contendo um único byte mutável ou número inteiro de 16 bits. As provas automatizadas são muito mais valiosas.
31818 Frank Hileman
1
@cmaster Você resumiu muito bem a diferença entre um teste e uma prova. Obrigado!
31818 Frank Hileman # 03:
2

Acho testes muito ridículos raramente úteis. Na maioria das vezes, acabo reimplementando o comportamento que a classe original já possui, o que derrota totalmente o objetivo da zombaria.

Para mim, uma estratégia melhor é ter uma boa separação de preocupações (por exemplo, você pode testar a Parte A do seu aplicativo sem inserir as partes B a Z). Uma arquitetura tão boa realmente ajuda a escrever um bom teste.

Além disso, estou mais do que disposto a aceitar efeitos colaterais, desde que eu possa revertê-los, por exemplo, se meu método modificar dados no banco de dados, deixe-o! Contanto que eu possa reverter o banco de dados para o estado anterior, qual é o problema? Além disso, há o benefício que meu teste pode verificar se os dados têm a aparência esperada. DBs na memória ou versões de teste específicas do dbs realmente ajudam aqui (por exemplo, versão de teste na memória do RavenDBs).

Por fim, gosto de zombar dos limites do serviço, por exemplo, não faça essa chamada http para o serviço b, mas vamos interceptá-lo e introduzir um método apropriado.

Christian Sauer
fonte
1

Eu gostaria que as pessoas de ambos os campos entendessem que testes de classe e testes de comportamento não são ortogonais.

Os testes de classe e de unidade são usados ​​de forma intercambiável e talvez não devam ser. Por acaso, alguns testes de unidade são implementados nas aulas. Isso é tudo. O teste de unidade acontece há décadas em idiomas sem classes.

Quanto aos comportamentos de teste, é perfeitamente possível fazer isso dentro dos testes de classe usando a construção GWT.

Além disso, se seus testes automatizados prosseguem ao longo das linhas de classe ou de comportamento depende das suas prioridades. Alguns precisarão prototipar rapidamente e obter algo fora da porta, enquanto outros terão restrições de cobertura devido a estilos internos. Muitas razões. Ambas são abordagens perfeitamente válidas. Você paga seu dinheiro, faz sua escolha.

Então, o que fazer quando o código for quebrado. Se foi codificado para uma interface, apenas a concreção precisa ser alterada (junto com todos os testes).

No entanto, a introdução de um novo comportamento não precisa comprometer o sistema. Linux e outros estão cheios de recursos obsoletos. E coisas como construtores (e métodos) podem ser sobrecarregados de maneira feliz sem forçar a mudança de todo o código de chamada.

Onde o teste de classe vence é onde você precisa fazer uma alteração em uma classe que ainda não foi implementada (devido a restrições de tempo, complexidade ou qualquer outra coisa). É muito mais fácil começar uma aula se ela tiver testes abrangentes.

Cão belga
fonte
Onde você tem concreção, eu teria escrito a implementação . É um calque de outro idioma (eu acho que francês) ou existe uma distinção importante entre concreção e implementação ?
Peter Taylor
0

A menos que a interface do X tenha sido alterada, você não precisa alterar o teste de unidade para A porque nada relacionado a A mudou. Parece que você realmente escreveu um teste de unidade de X e A juntos, mas chamou de teste de unidade de A:

Ao escrever testes de unidade para A, você zomba de X. Em outras palavras, durante o teste de unidade A, você define (postula) que o comportamento da zombaria de X é X1.

Idealmente, a simulação de X deve simular todos os comportamentos possíveis de X, não apenas o comportamento que você espera de X. Portanto, não importa o que você realmente implemente em X, A já deve ser capaz de lidar com isso. Portanto, nenhuma alteração em X, além de alterar a própria interface, terá qualquer efeito no teste de unidade para A.

Por exemplo: Suponha que A seja um algoritmo de classificação e A forneça dados a serem classificados. A simulação de X deve fornecer um valor de retorno nulo, uma lista vazia de dados, um único elemento, vários elementos já classificados, vários elementos ainda não classificados, vários elementos já classificados, vários elementos ordenados ao contrário, listas com o mesmo elemento repetido, valores nulos misturados, um ridiculamente grande número de elementos e também deve lançar uma exceção.

Então, talvez X inicialmente retornasse dados classificados às segundas-feiras e listas vazias às terças-feiras. Mas agora, quando X retorna dados não classificados às segundas-feiras e lança exceções às terças-feiras, A não se importa - esses cenários já foram abordados no teste de unidade de A.

Moby Disk
fonte
e se o X renomear o índice na matriz retornada de foobar para foobarRandomNumber, como posso contar com isso? se você entender meu ponto de vista, este é basicamente o meu problema, renomeei uma coluna retornada de secondName para sobrenome, uma tarefa clássica, mas meu teste nunca saberá, pois é ridicularizado. Eu apenas tenho uma sensação tão estranha como se muitas pessoas nesta pergunta nunca
tivessem
O compilador deve ter detectado essa alteração e fornecer um erro no compilador. Se você estiver usando algo como Javascript, recomendo alternar para o Typecript ou usar um compilador como o Babel, que pode detectar essas coisas.
Moby Disk
E se eu estiver usando matrizes em PHP, Java ou Javascript, se você alterar um índice de matriz ou removê-lo, não forem compiladores dessas linguagens, o índice poderá estar aninhado no número 36 - pensado nível aninhado da matriz, portanto, acho que o compilador não é a solução para isso.
FantomX1 13/10
-2

Você tem que olhar para diferentes testes.

Os testes de unidade em si só testam o X. Eles estão lá para impedir que você altere o comportamento do X, mas não protegem o sistema inteiro. Eles garantem que você possa refatorar sua classe sem introduzir uma mudança de comportamento. E se você quebrar X, você quebrou ...

O A deve de fato zombar do X para seus testes de unidade e o teste com o Mock deve continuar passando mesmo depois que você o altera.

Mas há mais de um nível de teste! Existem também testes de integração. Esses testes existem para validar a interação entre as classes. Esses testes geralmente têm um preço mais alto, pois não usam zombaria para tudo. Por exemplo, um teste de integração pode realmente gravar um registro em um banco de dados, e um teste de unidade não deve ter dependências externas.

Além disso, se X precisa ter um novo comportamento, seria melhor fornecer um novo método que forneça o resultado desejado

Heiko Hatzfeld
fonte