Um exemplo de um caso de falha que você prefiraria capturar é que o objeto em teste usa uma camada de armazenamento em cache, mas não mantém os dados conforme necessário. Então, se você consultar o objeto, ele vai dizer "yup, eu tenho o novo nome e endereço", mas você quer que o teste falhe, porque ele não fez realmente o que deveria.
Como alternativa (e com vista para a violação de responsabilidade única), suponha que seja necessário persistir uma versão codificada em UTF-8 da string para um campo orientado a byte, mas na verdade persiste o Shift JIS. Algum outro componente vai ler o banco de dados e espera ver o UTF-8, daí o requisito. Em seguida, a ida e volta através desse objeto informará o nome e o endereço corretos, pois ele será convertido de volta do Shift JIS, mas o erro não será detectado pelo seu teste. Espera-se que seja detectado por algum teste de integração posterior, mas o objetivo dos testes unitários é detectar os problemas precocemente e saber exatamente qual componente é responsável.
If one of them is not doing what it's supposed to, then its own test case will fail and we can fix it and run the test battery again.
Você não pode assumir isso, porque se você não for cuidadoso, escreva um conjunto de testes mutuamente dependente. O "salva?" teste chama o método save que está testando e, em seguida, o método load para confirmar que ele foi salvo. O "carrega?" teste chama o método save para configurar o equipamento de teste e, em seguida, o método de carga que está testando para verificar o resultado. Ambos os testes dependem da exatidão do método que eles não estão testando, o que significa que nenhum deles realmente testa a exatidão do método que está testando.
A pista de que há um problema aqui é que dois testes que supostamente estão testando diferentes unidades realmente fazem a mesma coisa . Ambos chamam um setter seguido por um getter, depois checam se o resultado é o valor original. Mas você queria testar se o setter persiste os dados, não que o par setter / getter trabalhe em conjunto. Então você sabe que algo está errado, você só tem que descobrir o que e consertar os testes.
Se o seu código for bem projetado para testes unitários, haverá pelo menos duas maneiras de testar se os dados foram realmente persistidos corretamente pelo método em teste:
-
zombar da interface do banco de dados, e ter seu registro simulado o fato de que as funções apropriadas foram chamadas, com os valores esperados. Isso testa o método faz o que é suposto e é o teste unitário clássico.
-
passe um banco de dados real com exatamente a mesma intenção , para registrar se os dados foram ou não corretamente persistidos. Mas, em vez de ter uma função zombada que diz apenas "sim, recebi os dados corretos", o teste é lido diretamente do banco de dados e confirma que está correto. Este pode não ser o mais mais puro teste possível, porque um mecanismo de banco de dados inteiro é uma grande coisa para usar para escrever um mock glorificado, com mais chances de eu ignorar alguma sutileza que faz um teste passar mesmo que algo está errado (por exemplo, eu não deveria usar a mesma conexão de banco de dados para ler como foi usado para gravar, porque eu posso ver uma transação não confirmada). Mas testa a coisa certa, e pelo menos você sabe que ela precisamente implementa toda a interface do banco de dados sem ter que escrever qualquer código falso!
Por isso, é um mero detalhe de implementação de teste se eu leio os dados do banco de dados de teste pelo JDBC ou se eu zombar do banco de dados. De qualquer maneira, o ponto é que eu posso testar a unidade melhor isolando-a do que posso se eu permitir que ela conspire com outros métodos incorretos na mesma classe para parecer correta mesmo quando algo está errado. Portanto, eu quero usar qualquer meio conveniente para verificar se os dados corretos foram mantidos, diferente de confiar no componente cujo método eu estou testando.
Se o seu código não for bem projetado para testes unitários, talvez você não tenha escolha, pois o objeto cujo método você deseja testar pode não aceitar o banco de dados como uma dependência injetada. Nesse caso, a discussão sobre a melhor maneira de isolar a unidade em teste muda para uma discussão sobre o quão próximo é possível isolar a unidade em teste. A conclusão é a mesma, no entanto. Se você puder evitar conspirações entre unidades defeituosas, você o faz, sujeito ao tempo disponível e a qualquer outra coisa que você pense que seria mais eficaz em encontrar falhas no código.